在 SQL 中迭代日期

发布于 2024-09-16 20:47:56 字数 838 浏览 6 评论 0原文

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

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

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

谢谢你!

乔纳森

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

失与倦" 2024-09-23 20:47:56

试试这个:

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

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)) 
望她远 2024-09-23 20:47:56

在 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 的解决方案可能更干净一些,因为它将数据类型保留为日期时间,而不是将它们转换为字符串。对于其他一些评论,我将把它留在这里。

The way that you iterate through rows in SQL is that you don't. SQL is a set-based language which requires a whole different mindset from other procedural languages. If you're going to be working with SQL you really need to be able to make that shift in thinking to be successful.

Here's how I would handle this one:

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

Also, your table design looks like it violates normal database design standards. Your "work" column is really just a calculation between the StartTime and FinishTime. That makes it a duplication of the same data, which can cause all sorts of problems. For example, what do you do when your StartTime and FinishTime are 4 hours apart, but the "Work" says 5 hours?

To include dates with no work associated, you'll need to either handle that in the front end, or you'll need a "Calendar" table. It would have all of the dates in it and you would do a LEFT JOIN to that with your table. For example:

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

The calendar table also allows you to add additional information to the dates, such as a flag for holidays, "overtime" days (maybe work counts as time and a half on Sundays), etc.

NOTE: Charles Bretana's solution is probably a little bit cleaner since it keeps the data types as datetimes instead of turning them into strings. I'm going to leave this here though for some of the other comments.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文