【问题标题】:How to split the time range into multiple rows如何将时间范围拆分为多行
【发布时间】:2020-11-07 14:55:17
【问题描述】:

我想在 SQL Server 中按小时将日期/时间范围拆分为多行,但有一些问题。我当前的数据集如下所示:

EmployeeCode         StartDateTime           EndDateTime
843578             2017-05-14 8:30 AM     2017-05-14 11:36 PM
587123             2017-05-14 22:00 PM    2017-05-15 01:28  AM

我想要这样的东西作为我的结果表。请注意,我也想将不到一小时的块视为一个独立行。 (例如上午 8:30 - 上午 9:00 为一排。)

EmployeeCode         StartDateTime           EndDateTime
843578             2017-05-14 8:30 AM     2017-05-14 9:00 PM
843578             2017-05-14 9:00 AM     2017-05-14 10:00 AM 
843578             2017-05-14 10:00 AM    2017-05-14 11:00 AM 
843578             2017-05-14 11:00 AM    2017-05-14 11:36 AM 

587123             2017-05-14 22:00 PM     2017-05-14 23:00 PM
587123             2017-05-14 23:00 PM     2017-05-15 00:00 AM
587123             2017-05-15 00:00 AM     2017-05-15 01:00 AM
587123             2017-05-15 01:00 AM     2017-05-15 01:28 AM

我当前的代码仅拆分同一天内的日期/时间范围。例如,员工 587123 的时间范围在 22:00 - 23:00 停止拆分,不适用于第二天的时间范围。 如何更新我的代码以在午夜后捕获数据? (样本结果表的最后三行。)

这是我当前的代码

SELECT YT.EmployeeCode,
       CASE WHEN YT.StartDateTime > DT.StartDateTime THEN YT.StartDateTime ELSE DT.StartDateTime END AS StartDateTime,
       CASE WHEN YT.EndDateTime < DT.EndDateTime THEN YT.EndDateTime ELSE DT.EndDateTime END AS StartDateTime
FROM (VALUES(843578,CONVERT(datetime2(0),'2017-05-14T08:30:00'),CONVERT(datetime2(0),'2017-05-14T15:36:00')),
            (587123,CONVERT(datetime2(0),'2017-05-14T09:00:00'),CONVERT(datetime2(0),'2017-05-14T18:28:00')))YT(EmployeeCode,StartDateTime,EndDateTime)
     CROSS APPLY (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13),(14),(15),(16),(17),(18),(19),(20),(21),(22),(23))T(I)
     CROSS APPLY (VALUES(DATEADD(HOUR,T.I,CONVERT(time(0),'00:00:00')),DATEADD(HOUR,T.I+1,CONVERT(time(0),'00:00:00'))))V(StartTime,EndTime)
     CROSS APPLY (VALUES(DATETIMEFROMPARTS(YEAR(YT.StartDateTime),MONTH(YT.StartDateTime),DAY(YT.StartDateTime),DATEPART(HOUR,V.StartTime),DATEPART(MINUTE,V.StartTime),0,0),
                         DATETIMEFROMPARTS(YEAR(YT.StartDateTime),MONTH(YT.StartDateTime),DAY(YT.StartDateTime),DATEPART(HOUR,V.EndTime),DATEPART(MINUTE,V.EndTime),0,0)))DT(StartDateTime,EndDateTime)
WHERE YT.StartDateTime <= DT.EndDateTime
  AND YT.EndDateTime >= DT.StartDateTime;

当前代码看起来太复杂了,所以如果您知道更好的方法,请告诉我。 对此我将不胜感激。

【问题讨论】:

标签: sql sql-server datetime


【解决方案1】:

这是一个递归 CTE 解决方案:

with cte as (
    select 
        employeecode, 
        startdatetime, 
        dateadd(hour, 1, datetimefromparts(year(startdatetime), month(startdatetime), day(startdatetime), datepart(hour, startdatetime), 0, 0, 0)) enddatetime
        enddatetime maxdatetime
    from mytable
    union all
    select employeecode, enddatetime, dateadd(hour, 1, enddatetime), maxdatetime
    from cte
    where enddatetime < maxdatetime
)
select employeecode, startdatetime, 
    case when enddatetime < maxdatetime then enddatetime else maxdatetime end as enddatetime
from cte

基本上,CTE 的锚点使用datetimefrompart() 执行计算第一个范围的结束。然后我们迭代生成以下范围,直到达到最大日期时间。然后我们可以使用外部查询显示结果,同时调整最后一个范围的结束日期。

【讨论】:

  • 谢谢@GMB,它似乎运作良好,但还有一个问题。它们不在我的示例数据中,但我的数据中确实有一些额外的变量,例如“班次类型”、“工资率”、“标志”等。如果我想包括这些额外的变量,我应该在哪里添加这些行在你的代码中?我是 SQL 新手,不想在您上面建议的代码中搞砸任何东西。
  • @alexsmith5123:您可以将它们添加到with 子句的每个成员(第一个和第二个select)以及外部select 中。
【解决方案2】:

我会使用递归 CTE 来解决这个问题:

with cte as (
      select t.EmployeeCode, t.StartDateTime as startdt, 
             dateadd(hour, datepart(hour, t.startdatetime) + 1, convert(datetime, convert(date, t.StartDateTime))) as enddt,
             t.endDateTime, 1 as lev
      from t
      union all
      select cte.employeecode, enddt,
             (case when dateadd(hour, 1, enddt) < enddatetime then dateadd(hour, 1, enddt) else enddatetime end),
             enddatetime, lev + 1
      from cte
      where enddt < enddatetime
     )
select *
from cte
order by employeecode, startdt;

Here 是一个 dbfiddle。

如果您的跨度可能超过 100 小时,那么您需要 option (maxrecursion 0) 进行查询。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-10-19
    • 1970-01-01
    • 1970-01-01
    • 2020-02-28
    • 2013-11-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多