扩展表中的多个时间序列

时间:2018-01-22 18:22:30

标签: sql sql-server time-series

我有一个包含多个时间序列的表,每个时间序列都与给定的ID相关联。

像这样的东西

+--------------------------------------+
| ID | start      | end        | value |
+--------------------------------------+
| a  | 01/01/2018 | 03/01/2018 | 5     |
| a  | 03/01/2018 | 04/01/2018 | 6     |
| a  | 04/01/2018 | 06/01/2018 | 7     |
| b  | 01/01/2018 | 04/01/2018 | 3     |
| b  | 04/01/2018 | 06/01/2018 | 4     |
+--------------------------------------+

我们看到时间序列是由不规则的间隔定义的。我想"扩展"每个时间序列,所以系列中每天都有一行。

喜欢这个

+--------------------------------------+
| ID | start      | end        | value |
+--------------------------------------+
| a  | 01/01/2018 | 02/01/2018 | 5     |
| a  | 02/01/2018 | 03/01/2018 | 5     |
| a  | 03/01/2018 | 04/01/2018 | 6     |
| a  | 04/01/2018 | 05/01/2018 | 7     |
| a  | 05/01/2018 | 06/01/2018 | 7     |
| b  | 01/01/2018 | 02/01/2018 | 3     |
| b  | 02/01/2018 | 03/01/2018 | 3     |
| b  | 03/01/2018 | 04/01/2018 | 3     |
| b  | 04/01/2018 | 05/01/2018 | 4     |
| b  | 05/01/2018 | 06/01/2018 | 4     |
+--------------------------------------+

在SQL中可以从前者获得后一个表,如果可以,请指出我正确的方向

注意:每个时间序列都是连续的,没有重叠的间隔。

3 个答案:

答案 0 :(得分:2)

你可以试试这个。

DECLARE @T TABLE (ID VARCHAR(2),  start DATE, [end] DATE, value INT )
INSERT INTO @T VALUES
( 'a', '01/01/2018', '01/03/2018', 5 ),
( 'a', '01/03/2018', '01/04/2018', 6 ),
( 'a', '01/04/2018', '01/06/2018', 7 ),
( 'b', '01/01/2018', '01/04/2018', 3 ),
( 'b', '01/04/2018', '01/06/2018', 4 )


;WITH CTE AS (
    SELECT * FROM @T 
    UNION ALL
    SELECT T.ID, DATEADD(DAY,1, CTE.start) Start, T.[end], T.value 
    FROM @T T 
        INNER JOIN CTE ON T.ID = CTE.ID AND T.value = CTE.value 
            AND DATEADD(DAY,1, CTE.start)  < T.[end]
)
SELECT ID , start, DATEADD(DAY,1, start) [end], value FROM CTE
ORDER BY ID, start

结果:

ID   start      end        value
---- ---------- ---------- -----------
a    2018-01-01 2018-01-02 5
a    2018-01-02 2018-01-03 5
a    2018-01-03 2018-01-04 6
a    2018-01-04 2018-01-05 7
a    2018-01-05 2018-01-06 7
b    2018-01-01 2018-01-02 3
b    2018-01-02 2018-01-03 3
b    2018-01-03 2018-01-04 3
b    2018-01-04 2018-01-05 4
b    2018-01-05 2018-01-06 4

答案 1 :(得分:1)

此方法将原始表与0中的整数表连接起来,仅选择那些小于日期之差的整数。选中的每一行都提供扩展日期范围的记录之一。我假设原始数据中的日期间隔不超过9999天,但如果是,您可以通过为tenthousands等添加一行来扩展整数表。

SELECT  T.ID, 
        DATEADD(D, V.N, T.Start) [start], 
        DATEADD(D, V.N+1, T.Start) [end], 
        T.Value
    FROM YourTable T
    JOIN (
                SELECT ones.n + 10*tens.n + 100*hundreds.n + 1000*thousands.n N
                    FROM (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) ones(n),
                         (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) tens(n),
                         (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) hundreds(n),
                         (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) thousands(n)

        ) V
    ON V.N < DATEDIFF(D, T.Start, T.[End])
编辑:我应该已经确认@slartidan用于在此答案中生成整数列表的优雅方式https://stackoverflow.com/a/33146869/1992793

答案 2 :(得分:0)

交叉申请通常是一个更快的选择。我假设你可以通过假设一个范围的月数限制来逃避,并假设你的输入日期总是在这个月的第一天。

with mnths as (
    select *
    from (values (0), (1), (2), (3), (4), (5), (6), (7), (8), (9), (10), (11)) as t(offset)
)
select *
from data
    cross apply (
        select dateadd(month, offset, start_dt) as expanded_start_dt
        from mnths
        where dateadd(month, offset, start_dt) < end_dt
    ) as m