Sql 如何将时间范围拆分为多行

Sql 如何将时间范围拆分为多行,sql,sql-server,datetime,Sql,Sql Server,Datetime,我想在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 我想要这样的结果表。请注意,我还

我想在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;
当前的代码看起来太复杂了,所以如果你知道更好的方法,请告诉我。
我将非常感谢您在这方面的帮助。

我将使用递归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;
他是一把小提琴


如果跨度可能超过100小时,则需要选项maxrecursion 0进行查询。

以下是递归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:你可以将它们添加到包含第一个和第二个select的子句的每个成员中,也可以添加到外部select中。