我有一个看起来有点像这样的数据表:
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控件。
有人可以给我一些线索,教程链接或其他一些帮助吗?否则我想我会在这几天摆弄它!
谢谢!
乔纳森
答案 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的解决方案可能有点清晰,因为它将数据类型保留为日期时间而不是将它们转换为字符串。对于其他一些评论,我将把这个留在这里。