【问题标题】:SQL Server filling gaps in time seriesSQL Server 填补时间序列中的空白
【发布时间】:2021-03-10 15:19:33
【问题描述】:

我在处理 SQL 请求时遇到问题。

我有两张表,一张代表日期向量,另一张代表不同证券价格的时间序列:

日期:

DateId Date
1 2021-01-01
2 2021-01-02
3 2021-01-03

时间序列:

SecurityId DateId Value
1 1 0.25
1 3 0.32
2 1 0.41
2 2 0.67

时间序列可能存在间隙,例如在上表中,SecurityId=1 在 DateId=2 处没有任何行,而 SecurityId=2 在 DateId=3 处没有任何行。

我需要构建一个请求,用时间序列中的最后一个值填补空白。 导致所有证券和所有日期的值如下:

SecurityId DateId Value
1 1 0.25
1 2 0.25
1 3 0.32
2 1 0.41
2 2 0.67
2 3 0.67

我尝试使用正确的连接开始匹配所有日期

 SELECT  * from [TimeSerie] px RIGHT JOIN Dates dt on dt.DateId = px.Dateid

不幸的是,这不起作用,因为总有一个安全性与 DateId 上的相等性匹配,所以我没有得到没有值的 TimeSeries 行。

我正在研究 SQL Server 标准 2019,我的目标是基于单个查询的解决方案(避免使用临时表或游标的过程)。

【问题讨论】:

    标签: sql sql-server join gaps-in-data


    【解决方案1】:

    您可以使用cross join 生成行,然后使用left join

    select d.*, s.*, ts.value
    from dates d cross join
         (select distinct securityid from timeseries) s left join
         (select ts.*,
                 lead(ts.dateid) over (partition by securityid order by dateid) as next_dateid
          from timeseries ts
         ) ts
         on s.securityid = ts.securityid and
            d.dateid >= ts.dateid and
            (d.dateid < ts.next_dateid or ts.next_dateid is null);
    

    这假设dateids 是连续的,这似乎是一个合理的假设。

    【讨论】:

    • 您好,Gordon,这真是一个很棒的解决方案,又快又简单。我不知道铅的用途,所以我学到了一些东西。我已经尝试过了,即使从性能的角度来看,它似乎也能很好地工作。也感谢之前编辑过这个问题。
    【解决方案2】:

    一种方法是使用 CTE 和窗口化 COUNT 将数据分组,然后获取该组的 MAX 值:

    --Sample data
    WITH Dates AS(
        SELECT *
        FROM (VALUES(1,CONVERT(date,'2021-01-01')),
                    (2,CONVERT(date,'2021-01-02')),
                    (3,CONVERT(date,'2021-01-03')))D(DateID,[Date])),
    TimeSerie AS(
        SELECT *
        FROM (VALUES(1,1,0.25),
                    (1,3,0.32),
                    (2,1,0.41),
                    (2,2,0.67))V(SecurityID,DateID,[Value])),
    --Solution
    DateSeries AS(
        SELECT DISTINCT
               D.DateID,
               D.[Date],
               TS.SecurityID
        FROM Dates D
             CROSS JOIN TimeSerie TS),
    Groups AS(
        SELECT DS.SecurityID,
               DS.DateID,
               TS.[value],
               COUNT(TS.[Value]) OVER (PARTITION BY DS.SecurityID ORDER BY [Date]) AS Grp
        FROM DateSeries DS
             LEFT JOIN TimeSerie TS ON DS.SecurityID = TS.SecurityID
                                   AND DS.DateID = TS.DateID)
    SELECT G.SecurityID,
           G.DateID,
           MAX([Value]) OVER (PARTITION BY G.SecurityID, G.Grp) AS [Value]
    FROM Groups G;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-12-23
      • 2019-02-05
      • 1970-01-01
      • 1970-01-01
      • 2012-02-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多