在SQL中迭代日期

时间:2010-08-31 13:55:53

标签: sql tsql sql-server-2005 gaps-and-islands

我有一个看起来有点像这样的数据表:

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

这些日期范围都不应超过午夜 我想写一个SQL,它会给我以下输出,给定输入开始日期为2010-08-02,结束日期为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 

我可以忍受,实际上可能最终需要,任何没有相关工作的日子也会在结果集中表示,可能就像这样一行:

2010-08-05     NULL   0

我不太确定如何以与使用其他语言相同的方式迭代SQL中的日期。

为了给出一些上下文,它的输出最终会插入Stacked Chart .Net控件。

有人可以给我一些线索,教程链接或其他一些帮助吗?否则我想我会在这几天摆弄它!

谢谢!

乔纳森

2 个答案:

答案 0 :(得分:7)

试试这个:

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

让失踪的日子变得更难。

   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)) 
编辑,我正在重新审视使​​用公用表表达式(CTE)的解决方案。这不需要使用日期表。

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

答案 1 :(得分:5)

在SQL中迭代遍历行的方式是不这样做。 SQL是一种基于集合的语言,需要与其他过程语言完全不同的思维方式。如果你打算使用SQL,你真的需要能够在思考成功的过程中实现这一转变。

以下是我将如何处理这个问题:

SELECT
    CONVERT(VARCHAR(10), StartTime, 121) AS [date],
    name,
    SUM(work)
FROM
    My_Table
WHERE
    StartTime >= @start_date AND
    StartTime < DATEADD(dy, 1, @finish_date)
GROUP BY
    CONVERT(VARCHAR(10), StartTime, 121),
    name

此外,您的表格设计看起来违反了正常的数据库设计标准。您的“工作”列实际上只是StartTime和FinishTime之间的计算。这使得它成为相同数据的重复,这可能导致各种各样的问题。例如,当你的StartTime和FinishTime相隔4小时,你做什么,但“工作”说5小时?

要包含没有相关工作的日期,您需要在前端处理,或者您需要一个“日历”表。它会包含所有日期,你可以用你的桌子进行LEFT JOIN。例如:

SELECT
    CONVERT(VARCHAR(10), C.StartTime, 121) AS [date],
    MT.name,
    SUM(MT.work)
FROM
    Calendar C
LEFT JOIN My_Table MT ON
    MT.StartDate BETWEEN C.StartTime and C.FinishTime
WHERE
    C.StartTime >= @start_date AND
    C.StartTime < DATEADD(dy, 1, @finish_date)
GROUP BY
    CONVERT(VARCHAR(10), C.StartTime, 121),
    MT.name

日历表还允许您向日期添加其他信息,例如假期标志,“加班”日(可能是星期日的工作时间等),等等。

注意:Charles Bretana的解决方案可能有点清晰,因为它将数据类型保留为日期时间而不是将它们转换为字符串。对于其他一些评论,我将把这个留在这里。