微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如果为空,则保留前一个非空值

如何解决如果为空,则保留前一个非空值

我正在使用 sql Server。我想用前一个(最后一个)非空值替换表中的所有 NULL,以填充数据中的 NULL 空白。例如,假设我有一个表,其中一些值为 NULL:

DECLARE @Table TABLE(
        dt datetime2(0),v1 INT,v2 INT,v3 INT,v4 INT,v5 INT,v999 INT
)

INSERT INTO @Table (dt,v1,v2,v3,v4,v5,v999) SELECT '6/12/2021 03:45',3,4,8,5,NULL,2
INSERT INTO @Table (dt,v999) SELECT '6/12/2021 03:46',9,2,1,0
INSERT INTO @Table (dt,v999) SELECT '6/12/2021 03:47',7,NULL
INSERT INTO @Table (dt,v999) SELECT '6/12/2021 03:48',6,v999) SELECT '6/12/2021 03:49',v999) SELECT '6/12/2021 03:50',NULL

表格:

table

但是假设我有大约 200 万行和大约 200 列。

我已经为每一列尝试了 SELECT 语句,但速度非常慢。我也尝试过使用 UPDATE 语句(也使用 SELECT),但速度很慢。对于 sql Server,我找不到 LAST_VALUE 和 IGnorE NULLS 的替代方法。您对如何将 NULLS 替换为许多行和列的最后一个非空值有什么想法吗?

编辑:我希望结果看起来像这样,以前的非空值将为每列的任何计算空值填充:

result

我为每一列尝试了更新语句,但查询速度很慢。他们是这样的,但我尝试了几种。所有使用 Select 的尝试都非常缓慢。

UPDATE #table SET v1 = (SELECT TOP 1 u.v1 FROM #table u WHERE u.v1 is not null AND u.dt <= #table.dt ORDER BY u.dt DESC)

编辑 #2:为了问题的清晰度而进行编辑,因为我希望在列中的 NULL 间隙中“保留最后一个非值”。

解决方法

没有简单、廉价的方法可以做到这一点。部分问题在于您的数据模型。有这么多列是非常可疑的。而且,更糟糕的是,他们似乎拥有相似的数据。它们可能应该存储在不同的行中。

你能做什么?好吧,你可以这样做:

with toupdate as (
      select t.*,first_value(v1) over (order by (case when v1 is not null then 1 else 2 end),dt desc) as last_v1,first_value(v2) over (order by (case when v2 is not null then 1 else 2 end),dt desc) as last_v2,. . .
      from t
     )
update toupdate
    set v1 = coalesce(v1,last_v1),v2 = coalesce(v2,last_v2),. . . ;

我提醒您,更新大表中的所有行需要很长时间。但这是一种比较简单的表达查询的方式。

请注意,SQL Server 确实对查询或结果集中的列数有限制,因此这不适用于任意数量的列。

,

我一直在研究这个问题并想出一个解决方案。以下内容似乎适用于示例表。

我在 Tomaž Kaštrun 的博客上找到了解决方案的开始。他解释了用最后一个非 NULL 值替换所有 NULL 值的方法。

https://tomaztsql.wordpress.com/2018/08/05/filling-propagading-empty-values-with-last-nonnull-value-using-t-sql/

Tomaž 引用了 Itzik Ben-Gan 编写的名为“The Last non NULL Puzzle”的巧妙解决方案。它提供了使用连接和窗口函数的解决方案 2。关键是将日期转换为保留列顺序的二进制数。我不完全理解它,但它有效。

https://www.itprotoday.com/sql-server/last-non-null-puzzle

由于我的日期不是 smalldatetime 格式,我必须将我的日期格式转换为保留顺序的值才能使其正常工作。我在 stackoverflow 上找到了一篇文章,解释了如何将日期强制转换为日期 + 时间整数。

How to Convert datetime value to yyyymmddhhmmss in SQL server?

结合所有这些技术,我有一个如下所示的查询:

SELECT
dt,replace(convert(varchar(8),dt,112)+convert(varchar(8),114),':','') as dt_value,CAST(SUBSTRING(MAX(CAST(replace(convert(varchar(8),'') AS BINARY(32)) + 
     CAST(v1 AS BINARY(12))) 
     OVER( ORDER BY dt ASC ROWS UNBOUNDED PRECEDING ),33,12) AS INT) 
         AS v1_nonull,'') AS BINARY(32)) + 
     CAST(v2 AS BINARY(12))) 
     OVER( ORDER BY dt ASC ROWS UNBOUNDED PRECEDING ),12) AS INT) 
         AS v2_nonull,'') AS BINARY(32)) + 
     CAST(v3 AS BINARY(12))) 
     OVER( ORDER BY dt ASC ROWS UNBOUNDED PRECEDING ),12) AS INT) 
         AS v3_nonull,'') AS BINARY(32)) + 
     CAST(v4 AS BINARY(12))) 
     OVER( ORDER BY dt ASC ROWS UNBOUNDED PRECEDING ),12) AS INT) 
         AS v4_nonull,'') AS BINARY(32)) + 
     CAST(v5 AS BINARY(12))) 
     OVER( ORDER BY dt ASC ROWS UNBOUNDED PRECEDING ),12) AS INT) 
         AS v5_nonull,'') AS BINARY(32)) + 
     CAST(v999 AS BINARY(12))) 
     OVER( ORDER BY dt ASC ROWS UNBOUNDED PRECEDING ),12) AS INT) 
         AS v999_nonull

FROM @Table
ORDER BY dt

结果如下。我没有在我的全表上测试过这个,但结果看起来不错。

enter image description here

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。