Use of this data:
CREATE TABLE MyTable (ID INT, Date DATETIME, Allocation INT); INSERT INTO MyTable VALUES (1, {d '2012-01-01'}, 0); INSERT INTO MyTable VALUES (2, {d '2012-01-02'}, 2); INSERT INTO MyTable VALUES (3, {d '2012-01-03'}, 0); INSERT INTO MyTable VALUES (4, {d '2012-01-04'}, 0); INSERT INTO MyTable VALUES (5, {d '2012-01-05'}, 0); INSERT INTO MyTable VALUES (6, {d '2012-01-06'}, 5); GO
Try the following:
WITH DateGroups (ID, Date, Allocation, SeedID) AS ( SELECT MyTable.ID, MyTable.Date, MyTable.Allocation, MyTable.ID FROM MyTable LEFT JOIN MyTable Prev ON Prev.Date = DATEADD(d, -1, MyTable.Date) AND Prev.Allocation = 0 WHERE Prev.ID IS NULL AND MyTable.Allocation = 0 UNION ALL SELECT MyTable.ID, MyTable.Date, MyTable.Allocation, DateGroups.SeedID FROM MyTable JOIN DateGroups ON MyTable.Date = DATEADD(d, 1, DateGroups.Date) WHERE MyTable.Allocation = 0 ), StartDates (ID, StartDate, DayCount) AS ( SELECT SeedID, MIN(Date), COUNT(ID) FROM DateGroups GROUP BY SeedID ), EndDates (ID, EndDate) AS ( SELECT SeedID, MAX(Date) FROM DateGroups GROUP BY SeedID ) SELECT StartDates.StartDate, EndDates.EndDate, StartDates.DayCount FROM StartDates JOIN EndDates ON StartDates.ID = EndDates.ID;
The first section of the query is a recursive SELECT that binds to all rows that are alloc = 0 and whose previous day either does not exist or has a distribution! = 0. This effectively returns identifiers: 1 and 3 which are the starting dates of time periods, which you want to return to.
The recursive part of the same query starts with anchor strings and finds all subsequent dates that also have a distribution of = 0. SeedID tracks the bound identifier through all iterations.
The result so far is as follows:
ID Date Allocation SeedID ----------- ----------------------- ----------- ----------- 1 2012-01-01 00:00:00.000 0 1 3 2012-01-03 00:00:00.000 0 3 4 2012-01-04 00:00:00.000 0 3 5 2012-01-05 00:00:00.000 0 3
The following sub-query uses a simple GROUP BY to filter all start dates of each SeedID, as well as count days.
The last additional request does the same with the end dates, but this time the day count is not needed, since we already have this.
The final SELECT query combines the two together to combine the start and end dates and returns them along with the number of days.