Given a table of date ranges, e.g. Start Date
, End Date
, I've seen a few examples online of how to merge ranges on the fly, but I'm able to merge them as the user makes updates.
So for example, if 01/01
to 01/02
exists and a user inserts 15/01
to 15/02
, then the original range can be updated to 01/01
to 15/02
.
Is there a well known way for dealing with this, or do I need to work it all out and put it in a stored procedure?
The table looks like this…
CREATE TABLE [dbo].[dateRange](
[Id] [int] NOT NULL,
[Start] [datetime] NOT NULL,
[Finish] [datetime] NOT NULL
)
Given two ranges…
INSERT INTO dateRange (Id, Start, Finish) VALUES (1, '01-01-17', '01-02-17');
INSERT INTO dateRange (Id, Start, Finish) VALUES (2, '03-01-17', '01-04-17');
The user can delete, update and insert new ranges. Deletes are simple. I think there are three cases for inserts (updates have similar possibilities)…
-
New range covers an existing range…
INSERT INTO dateRange (Id, Start, Finish) VALUES (3, '15-12-16', '15-02-17');
Results in deletion of 1.
-
New range is covered by existing range. So…
INSERT INTO dateRange (Id, Start, Finish) VALUES (4, '15-01-17', '16-01-17');
.. is ignored.
-
New range partially overlaps a new range.
INSERT INTO dateRange (Id, Start, Finish) VALUES (5, '15-12-16', '15-01-17');
Insert, again, is ignored and the overlapped row is extended. It's more complicated if both ends of the new range are overlapped because one of the overlapped ranges needs to be deleted.
The window functions sound cool but I'm thinking of doing it in C#.
For every user, I want zero overlapping date ranges. I'm using SQL Server 2014.
Best Answer
In one of my comments to you
You said (paraphrasing)
Well, it's a slow work day today and since no one else has come on stage with an answer yet, I'll step up to the mic and prepare for the hecklers. This is a prototype without any performance considerations (indexes, etc). Even with proper indexing, it may not suit your needs. Naturally, if I have misunderstood your question (or worse, have a fatal flaw), go easy on the downvotes ;)
I tried to come at this problem with the KISS principle. My approach relies on a table trigger which deletes existing data and lays out a new timeline when inserts, updates and deletes are executed. It relies heavily on logic I got from Itzik Ben-Gan's post entitled New Solution to the Packing Intervals Problem. You should definitely read Itzik's post to fully understand the logic in my trigger.
Your original question indicated that "For every user, I want zero overlapping date ranges.". I took the liberty of adding a UserID column to your table. My table also uses an IDENTITY for the Id column. Since my solution blows away existing data and inserts a new timeline, I don't specify the Id column on my inserts.
Here's the trigger where the real fun happens
Now that we have our table with the trigger defined, let's insert some rows I'm inserting rows for TOM and JOE. My DML examples deal with TOM, but I wanted you to see that there was no magic up my sleeve in keeping updates isolated to whom they belong.
Each of the following cases build on the previous case (if one exists)
Case 1: New range covers an existing range...
Case 2: New range is covered by existing range. So...
Case 3: New range partially overlaps a new range.
Case 4: Update an existing row
Case 5: Insert a new range
Case 6: New range partially overlaps a new range.
Case 7: New range partially overlaps a new range.
Case 8: Delete an existing row
If you can achieve the same results in C# in an easier way, you should stick with that. Of course, a 'smartie' on this site may have a better solution.