SQL将多行中的字段的空值填充为先前的非空值
Posted
技术标签:
【中文标题】SQL将多行中的字段的空值填充为先前的非空值【英文标题】:SQL fill null values for a field in multiple rows as previous non null value 【发布时间】:2016-11-26 08:14:58 【问题描述】:我在 netezza 中有一个表(基于 postgresql),如下所示。我需要创建一个新表,其中 name
的 NULL 值替换为前一个非空行的 name
值。
table1
id name time value
---------------------
1 john 11:00 324
2 NULL 12:00 645
3 NULL 13:00 324
4 bane 11:00 132
5 NULL 12:00 30
6 NULL 13:00 NULL
7 NULL 14:00 -1
8 zane 11:00 152
9 NULL 12:00 60
10 NULL 13:00 NULL
输出表
name time value
---------------------
john 11:00 324
john 12:00 645
john 13:00 324
bane 11:00 132
bane 12:00 30
bane 13:00 NULL
bane 14:00 -1
zane 11:00 152
zane 12:00 60
zane 13:00 NULL
笔记:
由于权限限制,无法更改 table1,因此创建一个新表 是方式。
需要在Netezza
(最好)或MS Access
中运行。
用于在 Netezza 中创建测试数据的代码如下。
create temp table test (
id int
,name varchar(10)
,time time
,value int
)distribute on random;
insert into test (id, name, time, value) values(1, 'joe', '10:00', 324);
insert into test (id, name, time, value) values(2, null, '11:00', 645);
insert into test (id, name, time, value) values(3, null, '12:00', 324);
insert into test (id, name, time, value) values(4, 'bane', '10:00', 132);
insert into test (id, name, time, value) values(5, null, '11:00', 30);
insert into test (id, name, time, value) values(6, null, '12:00', null);
insert into test (id, name, time, value) values(7, null, '13:00', -1);
insert into test (id, name, time, value) values(8, 'zane', '10:00', 152);
insert into test (id, name, time, value) values(9, null, '11:00', 60);
insert into test (id, name, time, value) values(10, null, '12:00', null);
【问题讨论】:
【参考方案1】:试试这个递归 PostgreSQL 查询:
WITH RECURSIVE t(id, name, time, value) AS (
SELECT id, name, time, value FROM test WHERE id = (
SELECT MIN(id) FROM test
)
UNION
SELECT test.id, COALESCE(test.name, t.name), test.time, test.value
FROM test, t WHERE test.id = (
SELECT id FROM test WHERE id > t.id ORDER BY id LIMIT 1
)
) SELECT * FROM t ORDER BY id;
请注意,这可能会在每一行上发出SELECT
。如果你不想这样,你可以使用视图解决方案:
CREATE VIEW test_view AS
SELECT id, LAG(id) OVER (ORDER BY id) lag_id, name, time, value FROM test;
WITH RECURSIVE t(id, name, time, value) AS (
SELECT id, name, time, value FROM test_view WHERE lag_id IS NULL
UNION ALL
SELECT test_view.id, COALESCE(test_view.name, t.name),
test_view.time, test_view.value
FROM test_view, t WHERE test_view.lag_id = t.id
) SELECT * FROM t ORDER BY id;
这应该快得多。这个想法来自this article。 SQLFiddle:http://sqlfiddle.com/#!15/63f7b/1/1.
【讨论】:
这应该可以在 Postgresql 中使用。但是我需要在 Netezza(基于 Postgresql)上运行它并在运行它时收到以下错误 -ERROR: Recursive queries are not supported
。所以在这种情况下似乎不能使用递归。【参考方案2】:
这在 Access 2010 中适用于我:
SELECT
t1.id,
(
SELECT TOP 1 t2.name
FROM test t2
WHERE t2.id<=t1.id AND t2.name IS NOT NULL
ORDER BY t2.id DESC
) AS name,
t1.time,
t1.value
FROM test t1
它也应该适用于其他 SQL 方言,尽管它们的处理方式可能略有不同 TOP 1
(例如,LIMIT 1
是一个常见的变体)。
【讨论】:
【参考方案3】:您可以使用COALESCE
函数和子查询来实现这一点:
SELECT t.id,
COALESCE(t.name, (SELECT s.name FROM table s WHERE s.name IS NOT NULL AND s.id < t.id ORDER BY s.id LIMIT 1)) AS name,
t.time,
COALESCE(t.value, (SELECT s.value FROM table s WHERE s.value IS NOT NULL AND s.id < t.id ORDER BY s.id LIMIT 1)) AS value
FROM table t ORDER BY t.id
【讨论】:
这不起作用。错误:"SELECT" (at char 31) expecting an identifier found a keyword
哦,查询需要更多的括号。我会解决的。
错误:ERROR: (2) This form of correlated query is not supported
- 考虑重写。另请注意,我在Netezza
中运行它,它基于 PostgreSQL
这可能来自子查询中的多个结果行。我添加了排序和限制。
试过这个(在 netezza 上) - 但得到同样的错误 - ERROR: (2) This form of correlated query is not supported - consider rewriting
【参考方案4】:
基于 MS Access 的解决方案
SELECT (SELECT last(name)
FROM test AS temp
WHERE test.id >= temp.id AND temp.name IS NOT NULL) AS new_name, *
FROM test;
【讨论】:
【参考方案5】:尝试使用引导功能。不确定这是否适用于 postgre,对于 Oracle 则适用。我认为这会有所帮助。
【讨论】:
如何计算LEAD
所需的偏移量?
好问题。没想到。以上是关于SQL将多行中的字段的空值填充为先前的非空值的主要内容,如果未能解决你的问题,请参考以下文章
如何在 PySpark 中用该列的第一个非空值填充该列的空值
返回 MIN 和 MAX 值并忽略空值 - 使用前面的非空值填充空值