Iterating through dates in SQL

JonRed picture JonRed · Aug 31, 2010 · Viewed 9.7k times · Source

I have a table of data that looks a bit like this:

Name    StartTime              FinishTime              Work
Bob     2010-08-03 08:00:00    2010-08-03 12:00:00     4
Bob     2010-08-03 13:00:00    2010-08-03 16:00:00     3
Pete    2010-08-04 08:00:00    2010-08-04 12:00:00     4
Mark    2010-08-04 10:00:00    2010-08-04 12:00:00     2

None of these date ranges should ever span over midnight.
I want to write SQL that will give me the following output, given an input Start Date of 2010-08-02 and a Finish Date of 2010-08-05

Date          Name   TotalWork
2010-08-03    Bob    7
2010-08-03    Pete   3
2010-08-04    Pete   4
2010-08-04    Mark   2 

I could live with, and in fact may ultimately need, to have any days that do not have work associated also be represented in the results set, maybe as a row like this:

2010-08-05     NULL   0

I'm not quite sure how to iterate through dates in SQL in the same way that I would with other languages.

To give this some context, the output of this will ultimately plug into a Stacked Chart .Net control.

Could someone give me a clue, a link to a tutorial or some other help? Otherwise I think I'll be fiddling with this for days!

Thank you!

Jonathan

Answer

Charles Bretana picture Charles Bretana · Aug 31, 2010

Try this:

Select DateAdd(day, 0, DateDiff(day, 0, StartDate)) Date,
    Name, Sum (Work) TotalWork
From TableData
Group By Name, DateAdd(day, 0, DateDiff(day, 0, StartDate)) 

To get the missing days is harder.

   Declare @SD DateTime, @ED DateTime  -- StartDate and EndDate variables
   Select @SD = DateAdd(day, 0, DateDiff(day, 0, Min(StartDate))),
          @ED = DateAdd(day, 0, DateDiff(day, 0, Max(StartDate)))
   From TableData
   Declare @Ds Table (aDate SmallDateTime)
   While @SD <= @ED Begin 
       Insert @Ds(aDate ) Values @SD
       Set @SD = @SD + 1
   End 
-- ----------------------------------------------------
 Select DateAdd(day, 0, DateDiff(day, 0, td.StartDate)) Date,
    td.Name, Sum (td.Work) TotalWork
 From @Ds ds Left Join TableData td
    On DateAdd(day, 0, DateDiff(day, 0, tD.StartDate)) = ds.aDate 
 Group By Name, DateAdd(day, 0, DateDiff(day, 0, tD.StartDate)) 

EDIT, I am revisiting this with a solution that uses a Common Table Expression (CTE). This does NOT require use of a dates table.

    Declare @SD DateTime, @ED DateTime
    Declare @count integer = datediff(day, @SD, @ED)
    With Ints(i) As
      (Select 0 Union All
    Select i + 1 From Ints
    Where i < @count )  
     Select DateAdd(day, 0, DateDiff(day, 0, td.StartDate)) Date,
         td.Name, Sum (td.Work) TotalWork
     From Ints i 
        Left Join TableData d
           On DateDiff(day, @SD, d.StartDate) = i.i
     Group By d.Name, DateAdd(day, 0, DateDiff(day, 0, d.StartDate))