Sql server 使用T-SQL向前填充时间序列数据中的空值的有效方法

Sql server 使用T-SQL向前填充时间序列数据中的空值的有效方法,sql-server,tsql,Sql Server,Tsql,我有一个包含时间序列数据的表,其中大部分是空值,我想用最后一个已知值填充所有空值 我有一些解决方案,但它们比在Pandas中执行等效的DataFrame.fillnamethod='ffill'操作慢得多 我正在使用的代码/数据的简化版本: select d.[date], d.[price], (select top 1 p.price from price_table p where p.price is not null and p.[date] <=

我有一个包含时间序列数据的表,其中大部分是空值,我想用最后一个已知值填充所有空值

我有一些解决方案,但它们比在Pandas中执行等效的DataFrame.fillnamethod='ffill'操作慢得多

我正在使用的代码/数据的简化版本:

select d.[date], d.[price],
       (select top 1 p.price from price_table p
        where p.price is not null and p.[date] <= p.[date]
        order by p.[date] desc) as ff_price
from price_table d

我有超过1亿行,所以这需要相当长的时间。

这看起来像一个经典的缺口和孤岛问题。假设您没有使用2008或之前的版本,这些版本几乎完全不受支持,那么您应该会得到您想要的结果:

WITH CTE AS(
    SELECT [date],
           price,
           COUNT(CASE WHEN price IS NOT NULL THEN 1 END) OVER (ORDER BY [date]
                                                               ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS Grp
    FROM price_table p)
SELECT [date],
       price,
       MIN(price) OVER (PARTITION BY grp) AS ff_price
FROM CTE;
您还可以使用APPLY:


假设您的列是DATE,价格是小数5,2,请测试此方法:

SELECT
    P.[date],
    P.[price],
    ff_price = CONVERT(
        DECIMAL(5,2),       -- Original price datatype
        SUBSTRING(
            MAX(
                CAST(P.[date] AS BINARY(3)) +   -- 3: datalength of P.[date] column
                CAST(P.[price] AS BINARY(5))    -- 5: datalength of P.[price] column
            ) OVER (ORDER BY P.[date] ROWS UNBOUNDED PRECEDING),

            4,  -- Position to start that's not the binary part of the date

            5))-- Characters that compose the binary of the original price datatype
FROM
    price_table  AS P
这是我用一个类似的问题实现的解决方案,您可以找到一个令人信服的解释。这种方法之所以好,是因为它不需要显式排序,只要您有一个按日期排序的索引

它所做的基本上是使用一个带窗口的最大值,将构成日期列的3个字节串联起来。这就是为什么我提到列必须是日期,否则DATETIME将需要8个字节,您可以编辑查询以使用构成价格列的字节(也假设是5个字节)。这是CASTP.[date]作为二进制3+CASTP.[price]作为二进制5的部分

当您计算此值并按p.[date]行排序时,引擎基本上是使用最重要的字节为日期的值执行最大滚动。当日期更改时,max结果将始终更新,但考虑到将任何值与NULL作为价格进行串联也将产生NULL作为二进制,则max将始终忽略此值,并在P.[date]行之前无限保留以前的非NULL max

这是加窗最大值的二进制结果,我添加了一条带有NULL的前一条记录,因此您可以看到,对于NULL价格值,结果为NULL:

date        price   ff_price    WindowedMax
2016-07-10  NULL    NULL        NULL
2016-07-11  0.79    0.79        0x9B3B0B050200014F
2016-07-12  NULL    0.79        0x9B3B0B050200014F
2016-07-13  NULL    0.79        0x9B3B0B050200014F
2016-07-14  0.69    0.69        0x9E3B0B0502000145
2016-07-15  NULL    0.69        0x9E3B0B0502000145
2016-07-21  0.88    0.88        0xA53B0B0502000158
2016-07-22  NULL    0.88        0xA53B0B0502000158

这比其他解决方案快得多:针对1.35亿行,它在330行中完成了所有其他答案。这也是让我绞尽脑汁的最困难的方法,但现在我已经弄明白了它的工作原理,它看起来真的很聪明。很高兴你明白了我们的@user448830。ROWS-BETWEEN子句是对SQL Server 2012中添加的窗口函数的一个很好的补充。如果您理解了语法,希望将来能很好地使用它:无界前导行之间的行的用途是什么。。。部分我试着做一些类似的事情,没有那个部分我也能做。仅按日期排序就足够了。无界前一行和当前行之间的行是默认窗口@gouravkr,我只想定义它;我有一个类似的解决方案,我会在一个子查询中定义分组变量,然后将它的累积和取出来。这个解决方案通过少一层子查询实现了同样的效果。有趣的是,有多少种方法可以解决同一个问题。这个解决方案需要大量的摆弄才能开始工作,但力学对我来说是很有意义的。实际上,它的性能相当于@Larnu的解决方案23秒,比我最初的解决方案或应用方法要好得多。@user448830是的,这是一个隐蔽的解决方案,但在处理大数据集或性能时,学习这些技巧确实很有帮助。很高兴能帮忙:
SELECT
    P.[date],
    P.[price],
    ff_price = CONVERT(
        DECIMAL(5,2),       -- Original price datatype
        SUBSTRING(
            MAX(
                CAST(P.[date] AS BINARY(3)) +   -- 3: datalength of P.[date] column
                CAST(P.[price] AS BINARY(5))    -- 5: datalength of P.[price] column
            ) OVER (ORDER BY P.[date] ROWS UNBOUNDED PRECEDING),

            4,  -- Position to start that's not the binary part of the date

            5))-- Characters that compose the binary of the original price datatype
FROM
    price_table  AS P
date        price   ff_price    WindowedMax
2016-07-10  NULL    NULL        NULL
2016-07-11  0.79    0.79        0x9B3B0B050200014F
2016-07-12  NULL    0.79        0x9B3B0B050200014F
2016-07-13  NULL    0.79        0x9B3B0B050200014F
2016-07-14  0.69    0.69        0x9E3B0B0502000145
2016-07-15  NULL    0.69        0x9E3B0B0502000145
2016-07-21  0.88    0.88        0xA53B0B0502000158
2016-07-22  NULL    0.88        0xA53B0B0502000158