Sql server 使用T-SQL向前填充时间序列数据中的空值的有效方法
我有一个包含时间序列数据的表,其中大部分是空值,我想用最后一个已知值填充所有空值 我有一些解决方案,但它们比在Pandas中执行等效的DataFrame.fillnamethod='ffill'操作慢得多 我正在使用的代码/数据的简化版本: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] <=
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