views:

158

answers:

3

I have a table that consists of, among other things, two fields named StartTime and EndTime. Both are TIME fields.

I want to add a constraint preventing the insertion of any records that overlap with preexisting time ranges. E.g. if a record already exists with StartTime = 5:00, EndTime = 10:00, I would want an insert with StartTime = 6:00, EndTime = 9:00 to fail due to the overlap.

Is there any way to accomplish this, with or without triggers?

+4  A: 

I haven't tried it but I imagine something like this would work:

create trigger preventOverlaps
on infotable
FOR Insert, Update
As
DECLARE @Count int;
select @Count = count(*) from infotable where 
  (inserted.startdate > startDate && inserted.startdate < endDate) ||
  (inserted.endDate < endDate && inserted.endDate > startDate)
if(@Count > 0)
begin
   rollback transaction;
end
stimms
Would this work in SQL Server?
gbn
David Hall
+2  A: 

The trigger below should work - it is also possible to do this with check contraints, but the logic shown in this post kind of hurts my head.

CREATE TRIGGER [dbo].[DateRangeTrigger]
   ON  [dbo].[TargetTable]
   FOR INSERT, UPDATE
AS 
BEGIN

IF EXISTS (SELECT t.starttime, t.endtime FROM TargetTable t
     Join inserted i
     On (i.starttime > t.starttime AND i.starttime < t.endtime AND i.UniqueId <> t.UniqueId) 
           OR (i.endtime < t.endtime AND i.endtime > t.starttime AND i.UniqueId <> t.UniqueId)
           OR (i.starttime < t.starttime AND i.endtime > t.endtime AND i.UniqueId <> t.UniqueId)
        )
BEGIN
    RAISERROR ('Inserted date was within invalid range', 16, 1)
    IF (@@TRANCOUNT>0)
        ROLLBACK
END


END
David Hall
Perfect, thanks David! This works exactly like I wanted.
David Pfeffer
Whoops, one more question. Won't this only work for insert? You capture update also in the trigger, but I only see a case handling insertion.
David Pfeffer
Hmmmm - looking at this, I don't think the update case is possible without including some primary key to leverage. I've updated my answer with that.
David Hall
There's a problem in the join condition; "i.endtime < t.endtime AND i.endtime > t.endtime" will always evaluate to false.
Whatsit
Thanks whatsit - fixed that up now.
David Hall
Just added the final (that I can think of) required clause, to cover the total overlap case in the new record.
David Hall
A: 

This trigger will also work for situations where one timespan completely contains the other. For example, if there's an existing record for 6:00 - 9:00 and you try to insert one for 5:00 - 10:00.
(Based on David Hall's answer)

CREATE TRIGGER DateRangeOverlapTrigger
ON  TargetTable
FOR INSERT, UPDATE
AS 
BEGIN
IF EXISTS
    (SELECT t.UniqueId
    FROM TargetTable t
        JOIN inserted i ON i.starttime < t.endtime
            AND i.endtime > t.starttime
            AND i.UniqueId <> t.UniqueId)
BEGIN
    RAISERROR ('Invalid due to time overlap', 16, 1)
    IF (@@TRANCOUNT > 0)
        ROLLBACK
END
END
Whatsit
This trigger only works for instances that ONLY contain one another, not a partial overlap. Some combination of the two answers is needed.
David Pfeffer
@bytenik: I can't see what the problem is. Can you give a specific example that the trigger won't work for?
Whatsit