SQL时间序列完成脚本

时间:2016-05-14 02:22:48

标签: tsql sql-server-2014

版本: SQL Server 2014

目标:创建包含现有日期范围记录的完整时间序列。

初始数据设置:

IF OBJECT_ID('tempdb..#DataSet') IS NOT NULL
    DROP TABLE #DataSet;

CREATE TABLE #DataSet (
    RowID INT
    ,StartDt DATETIME
    ,EndDt DATETIME
    ,Col1 FLOAT);

INSERT INTO #DataSet (
    RowID
    ,StartDt
    ,EndDt
    ,Col1)
VALUES
    (1234,'1/1/2016','12/31/2999',100)
    ,(1234,'7/23/2016','7/27/2016',90)
    ,(1234,'7/26/2016','7/31/2016',80)
    ,(1234,'10/1/2016','12/31/2999',75);

期望的结果:

RowID, StartDt, EndDt, Col1
1234, '01/01/2016', '07/22/2016', 100
1234, '07/23/2016', '07/26/2016', 90
1234, '07/26/2016', '07/31/2016', 80
1234, '08/01/2016', '09/30/2016', 100
1234, '10/01/2016', '12/31/2999', 75

我承认,这不是一件容易的事,如果有人建议如何单独利用SQL(Microsoft 2014 TSQL)解决这个问题,我将不胜感激。请记住它是SQL,我们希望根据大型数据集的性能不惜一切代价避免使用游标。

先谢谢。

同样作为一个FYI我通过利用LEAD窗口函数将当前记录的结束日期设置为下一个的Startdate-1来实现一半。从以前的记录中填补空白的另一半仍然是我无法实现的。

更新了9月31日至9月30日。

1 个答案:

答案 0 :(得分:0)

以下查询基本上是您所要求的。您可以调整它以满足您的要求。请注意,在检查查询结果时,您所需的结果包含09/31/2016,这不是有效日期。

    WITH
    RankedData AS
    (
      SELECT RowID, StartDt, EndDt, Col1,
        DATEADD(day, -1, StartDt) AS PrevEndDt,
        RANK() OVER(ORDER BY StartDt, EndDt, RowID) AS rank_no
      FROM #DataSet
    ),
    HasGapsData AS
    (
      SELECT a.RowID, a.StartDt,
        CASE WHEN b.PrevEndDt <= a.EndDt THEN b.PrevEndDt ELSE a.EndDt END AS EndDt,
        a.Col1, a.rank_no
      FROM RankedData a
        LEFT JOIN RankedData b ON a.rank_no = b.rank_no - 1
    )
    SELECT RowID, StartDt, EndDt, Col1
    FROM HasGapsData
    UNION ALL
    SELECT a.RowID,
      DATEADD(day, 1, a.EndDt) AS StartDt,
      DATEADD(day, -1, b.StartDt) AS EndDt,
      a.Col1
    FROM HasGapsData a
      INNER JOIN HasGapsData b ON a.rank_no = b.rank_no - 1
    WHERE DATEDIFF(day, a.EndDt, b.StartDt) > 1
    ORDER BY StartDt, EndDt;