SQL Server游标类型的运行(没有游标?)

时间:2018-02-26 20:05:38

标签: sql-server

给出下表:

create table xx (start_time datetime, end_time datetime, label varchar(100));
insert into xx values
('20180101 08:00', '20180103 08:00', 'test 1'), 
('20180101 06:30', '20180101 08:00', 'test 2'), 
('20180101 10:00', '20180102 08:00', 'test 3');

我必须生成一个列表,其中记录的重复次数与start_time和end_time之间的天数一样多。 预期结果是:

run_date    label
2018-01-01  test 1
2018-01-02  test 1
2018-01-03  test 1
2018-01-01  test 2
2018-01-01  test 3
2018-01-02  test 3

如何以高效的方式实现这一目标(可能没有任何丑陋的光标)? 动态时间间隔未定义(1到10天之间) 源表非常大(几百万条记录)

2 个答案:

答案 0 :(得分:2)

如果您没有日历表,则一种方法是与CROSS APPLY一致的临时计数表

示例

Select run_date=cast(B.D as date)
      ,A.label
 from XX A
 Cross Apply (
                Select Top (DateDiff(DAY,Start_Time,End_Time)+1) D=DateAdd(DAY,-1+Row_Number() Over (Order By (Select Null)),Start_Time) 
                 From  master..spt_values n1 -- ,master..spt_values n2  -- remove comment if span > 6 years
             ) B

<强>返回

run_date    label
2018-01-01  test 1
2018-01-02  test 1
2018-01-03  test 1
2018-01-01  test 2
2018-01-01  test 3
2018-01-02  test 3
  

修改

刚注意到数百万条记录。使用JOIN到日历表

可能会更好

答案 1 :(得分:1)

这是Numbers表派上用场的地方之一。 Here Aaron Bertrand的一篇文章谈到了这些表格。

基本上,您创建一个表Numbers,其中包含一行Number,其中包含N行,从0(或1)到您想要的最大数量(以及基础类型支持)。然后你可以轻松加入:

SELECT CONVERT(date, xx.start_time + n.Number) AS RunningDate, xx.label
FROM xx
INNER JOIN Numbers n ON n.Number <= DATEDIFF(DAY, xx.start_time, xx.end_time)

此解决方案假设Numbers表从0开始。应略微修改以允许从1开始的表相同。