2013-03-26 6 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

+0

Я бы рекомендовал триггер, а не ограничение на уровне таблицы. Вам также нужно следить за обновлениями? –

+0

@Hotchips вы не можете использовать хранимую процедуру для защиты данных, потому что люди могут получить доступ к таблице, не пройдя хранимую процедуру. Конечно, люди тоже могут отключать триггеры, но это преднамеренная злоба (и если это худшее, кто-то с такой большой привилегией вредит вашей базе данных, подумайте, что вам очень повезло). Вы также можете использовать хранимые процедуры *, но они не должны быть единственной защитой. –

ответ

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).

Смежные вопросы