遍历行和爆炸日期范围

Loop through rows and exploding date range

我有一个任务 table,我想在其中创建一个可以执行以下操作的视图: 1.遍历每一行 2. 将开始日期和结束日期之间的每一天分解成新的一行 3. 在新列中插入平均任务工时

这是table:

CREATE TABLE #InputTABLE
(
TaskID varchar (200),
startdate DATETIME,
enddate DATETIME,
TaskWork int 
)

INSERT INTO #InputTABLE VALUES('2298aas','2018-06-06','2018-06-12',200);

到目前为止,我已经解决了 2 和 3,但我真的很难处理迭代部分。如果 InputTABLE 中存在不止一行,下面的代码将失败:

CREATE TABLE #OutputTABLE
(
TaskID varchar (200),
startdate DATETIME,
enddate DATETIME,
TaskWork int 
)

DECLARE @taskid varchar (200)
DECLARE @cnt int
DECLARE @startDate datetime
DECLARE @endDate datetime
DECLARE @incr int
DECLARE @tempDate datetime 
DECLARE @work int
DECLARE @averagework int

SET @taskid=(Select TaskID from #InputTABLE)
SET @startDate=(Select startdate from #InputTABLE)
SET @endDate=(Select enddate from #InputTABLE)
SET @cnt=DATEDIFF(dy,@startDate,@endDate)
SET @incr=0

SET @tempDate=DATEADD(dy,@incr,Cast(@startDate As datetime))
SET @work=(Select TaskWork from #InputTABLE)
SET @averagework= @work/@cnt

WHILE @cnt>=0
BEGIN

   IF @cnt = 0 
      BEGIN
         INSERT INTO #OutputTABLE VALUES(@taskid,@tempDate,@endDate,@averagework);
      END
   ELSE
      BEGIN
         insert into #OutputTABLE values(@taskid,@tempDate,DATEADD(dy, DATEDIFF(dy,0,@tempDate)+1, -1),@averagework);
      END
   SET @tempDate=DATEADD(dy,@incr+1,DATEADD(dy,DATEDIFF(dy,0,@startDate),0))

   SET @cnt=@cnt-1
   SET @incr=@incr+1

   END

我考虑过使用来自 this 的游标来实现该解决方案,但我不确定该怎么做? 我也担心关于循环遍历行的评论对性能不利,因此非常感谢任何关于这方面的建议!

table 大约有 15,000 行,平均日期范围大约为 31 天,因此视图大约有 465,000 行。数字不高,但 table 正在不断增长,因此我可能还需要将视图限制在过去两年内。

你的问题不是很清楚,但我的魔法球告诉我,你可能正在寻找这个:

SET DATEFORMAT ymd;
CREATE TABLE #InputTABLE
(
TaskID varchar (200),
startdate DATETIME,
enddate DATETIME,
TaskWork int 
);

INSERT INTO #InputTABLE VALUES('six days','2018-06-06','2018-06-12',200)
                             ,('one day','2018-06-06','2018-06-07',200);

SELECT TaskID
      ,DATEADD(DAY,B.Numbr,startdate) AS ExplodingDate
      ,CAST(TaskWork AS DECIMAL(10,4))/A.DayDiff
FROM #InputTABLE
CROSS APPLY(SELECT DATEDIFF(DAY,startdate,enddate) AS DayDiff) A
CROSS APPLY(SELECT TOP (A.DayDiff) ROW_NUMBER() OVER(ORDER BY (SELECT NULL))-1 AS Numbr FROM master..spt_values) B

DROP TABLE #InputTABLE;

结果

TaskID      ExplodingDate           (Kein Spaltenname)
six days    2018-06-06 00:00:00.000 33.333333333333333
six days    2018-06-07 00:00:00.000 33.333333333333333
six days    2018-06-08 00:00:00.000 33.333333333333333
six days    2018-06-09 00:00:00.000 33.333333333333333
six days    2018-06-10 00:00:00.000 33.333333333333333
six days    2018-06-11 00:00:00.000 33.333333333333333
one day     2018-06-06 00:00:00.000 200.000000000000000

简短说明
第一个 APPLY 计算两个日期之间的天数差异。
第二个 APPLY 使用 TOPROW_NUMBER 的技巧来动态创建 tally table
这将为每个输入行创建尽可能多的行,因为开始日期和结束日期之间有天数。
剩下的就是简单的计算了...

更新具有持久性的完整示例table

CREATE DATABASE TestDB;
GO
USE TestDB;
GO
CREATE TABLE dbo.RunningNumbers(Number INT NOT NULL
                               ,CalendarDate DATE NOT NULL
                               ,CalendarYear INT NOT NULL
                               ,CalendarMonth INT NOT NULL
                               ,CalendarDay INT NOT NULL
                               ,CalendarWeek INT NOT NULL
                               ,CalendarYearDay INT NOT NULL
                               ,CalendarWeekDay INT NOT NULL);

DECLARE @CountEntries INT = 100000;
DECLARE @StartNumber INT = 0;


WITH E1(N) AS(SELECT 1 FROM(VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))t(N)), --10 ^ 1
    E2(N) AS(SELECT 1 FROM E1 a CROSS JOIN E1 b), -- 10 ^ 2 = 100 rows
    E4(N) AS(SELECT 1 FROM E2 a CROSS JOIN E2 b), -- 10 ^ 4 = 10,000 rows
    E8(N) AS(SELECT 1 FROM E4 a CROSS JOIN E4 b), -- 10 ^ 8 = 10,000,000 rows
    CteTally AS
    (
        SELECT TOP(ISNULL(@CountEntries,1000000)) ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) -1 + ISNULL(@StartNumber,0) As Nmbr
        FROM E8
    )
INSERT INTO dbo.RunningNumbers
SELECT CteTally.Nmbr,CalendarDate.d,CalendarExt.*
FROM CteTally
CROSS APPLY
(
    SELECT DATEADD(DAY,CteTally.Nmbr,{ts'1900-01-01 00:00:00'})
) AS CalendarDate(d)
CROSS APPLY
(
    SELECT YEAR(CalendarDate.d) AS CalendarYear
          ,MONTH(CalendarDate.d) AS CalendarMonth
          ,DAY(CalendarDate.d) AS CalendarDay
          ,DATEPART(WEEK,CalendarDate.d) AS CalendarWeek
          ,DATEPART(DAYOFYEAR,CalendarDate.d) AS CalendarYearDay
          ,DATEPART(WEEKDAY,CalendarDate.d) AS CalendarWeekDay
) AS CalendarExt;
GO

SET DATEFORMAT ymd;
CREATE TABLE #InputTABLE
(
TaskID varchar (200),
startdate DATETIME,
enddate DATETIME,
TaskWork int 
);

INSERT INTO #InputTABLE VALUES('six days','2018-06-06','2018-06-12',200)
                             ,('one day','2018-06-06','2018-06-07',200);

SELECT TaskID
      ,B.CalendarDate
      ,CAST(TaskWork AS DECIMAL(10,4))/(A.DayDiff+1)
FROM #InputTABLE
CROSS APPLY(SELECT DATEDIFF(DAY,startdate,enddate) AS DayDiff) A
CROSS APPLY(SELECT * FROM dbo.RunningNumbers WHERE CalendarDate BETWEEN startdate AND enddate) B

DROP TABLE #InputTABLE;
GO

USE master;
GO

DROP DATABASE TestDB;