2013-03-26 89 views
0

我想为我的表创建检查约束,如果新预订范围(start_date,end_date)与已提交的行相交,则无法添加新行。 但我不能将查询放在检查约束中。你有一个想法如何做到这一点?select in check constraint

的表格:

APARTMAN 

     id INT 
     price INT 

BOOKINGS 

     id INT 
     start_date DATE 
     end_date DATE 
     apartman_id INT 

[apartman_id] IN (SELECT [id] FROM [dbo].[APARTMAN] 
    WHERE [id] NOT IN (
     SELECT [apartman_id] FROM 
      [dbo].[BOOKINGS] 
     WHERE 
      ([start_date] <= "requested end_date" AND 
      [end_date] >= "requested start_date") 
      OR 
      ([start_date] <= "requested start_date" AND 
      [end_date] >= "requested end_date") 
      OR 
      (([start_date] <= "requested end_date" AND [end_date] >= "requested start_date") 
       OR 
       ([end_date] <= "requested start_date" AND [end_date] >= "requested end_date")) 
    ) 
) 
+0

从另一个角度来看待它,是否可以通过存储过程完成插入操作? – Hotchips 2013-03-26 23:24:12

+0

我会推荐一个触发器而不是表级约束。你还必须保护更新吗? – 2013-03-26 23:35:10

+0

@Hotchips您无法真正使用存储过程来保护数据,因为人们可以在不通过存储过程的情况下访问表。当然,人们也可以禁用触发器,但这是故意的恶意行为(如果这是那些有这么多特权的人最坏的事情会伤害你的数据库,那么认为自己很幸运)。你也可以使用存储过程*,但它们不应该是唯一的防线。 – 2013-03-26 23:37:53

回答

2

下面是一个替代触发器,我想处理所有的场景。

CREATE TRIGGER dbo.PreventOverlappingBookings 
ON dbo.BOOKINGS INSTEAD OF INSERT, UPDATE 
AS 
BEGIN 
    SET NOCOUNT ON; 

    IF EXISTS (
    SELECT 1 FROM inserted AS i 
     INNER JOIN dbo.BOOKINGS AS b 
     ON (b.id <> i.id OR i.id = 0) -- 0 for insert 
     AND b.apartman_id = i.apartman_id 
     AND ((b.start_date <= i.end_date AND b.end_date >= i.start_date) 
     OR (b.start_date <= i.start_date AND b.end_date >= i.end_date) 
     OR (b.end_date <= i.start_date AND b.end_date >= i.end_date)) 
) OR EXISTS (
    -- also make sure there are no overlaps in a set-based insert/update 
    SELECT 1 FROM inserted AS i 
     INNER JOIN inserted AS b 
     ON (b.id <> i.id OR i.id = 0) -- 0 for insert 
     AND b.apartman_id = i.apartman_id 
     AND ((b.start_date <= i.end_date AND b.end_date >= i.start_date) 
     OR (b.start_date <= i.start_date AND b.end_date >= i.end_date) 
     OR (b.end_date <= i.start_date AND b.end_date >= i.end_date)) 
) 
    BEGIN 
    RAISERROR('Overlapping date range.', 11, 1); 
    END 
    ELSE 
    BEGIN 
     UPDATE b SET start_date = i.start_date, end_date = i.end_date 
     FROM dbo.BOOKINGS AS b 
     INNER JOIN inserted AS i 
     ON b.id = i.id; 
     IF @@ROWCOUNT > 0 
     BEGIN 
     INSERT dbo.BOOKINGS(start_date, end_date, apartman_id) 
      SELECT start_date, end_date, apartman_id FROM inserted AS i; 
     END 
    END 
END 
GO 

一些答案will suggest a function in a UDF,但I don't trust them (and neither should you, IMHO)