6

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

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 控件中。

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

谢谢!

乔纳森

4

2 回答 2

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)) 
于 2010-08-31T14:00:59.253 回答
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 的解决方案可能更简洁一些,因为它将数据类型保留为日期时间,而不是将它们转换为字符串。不过,对于其他一些评论,我将把它留在这里。

于 2010-08-31T14:01:54.797 回答