用 Redshift 中的第一个非空跟随值填充缺失值
Posted
技术标签:
【中文标题】用 Redshift 中的第一个非空跟随值填充缺失值【英文标题】:Fill missing values with first non-null following value in Redshift 【发布时间】:2014-07-01 23:01:22 【问题描述】:我正在使用 Redshift。给定以下数据:
CREATE TABLE test (
id INT,
val1 INT,
val2 INT
);
INSERT INTO test VALUES
(1, 0, NULL),
(2, 0, NULL),
(3, 13, 1),
(4, 0, NULL),
(5, 0, NULL),
(6, 0, NULL),
(7, 0, NULL),
(8, 21, 2),
(9, 0, NULL),
(10, 143,3)
;
我想用后面的第一个非空值填充缺失的 val2 值,例如
INSERT INTO results VALUES
(1, 0, 1),
(2, 0, 1),
(3, 13, 1),
(4, 0, 2),
(5, 0, 2),
(6, 0, 2),
(7, 0, 2),
(8, 21, 2),
(9, 0, 3),
(10,143,3)
;
在 Redshift/Postgres 8.0.2 中实现此目的的最佳方法是什么?
【问题讨论】:
感谢您特别提到您使用的是 Redshift,而不仅仅是“PostgreSQL”。通常的策略是加入generate_series
,但我不知道你在 Redshift 上是否有,而且亚马逊不提供 SQLFiddle 账户,所以我无法真正测试。
很遗憾没有 generate_series
... 通常的解决方法是使用过程语言(不在 Redshift 上)、窗口函数(不在 Redshift 上)等。
@CraigRinger 窗口函数在 redshift 中受支持。无论如何,我希望看到 generate_series() 的解决方案。
【参考方案1】:
我能够解决它的一种方法(利用非空 val2 值是连续的这一事实)如下。不过性能糟糕,所以任何更好的解决方案都会受到欢迎。
SELECT
t1.id
, t1.val1
, COALESCE(t1.val2, MIN(t2.val2)) as val2
FROM test t2 LEFT JOIN test t1 ON t2.id >= t1.id
WHERE t2.val2 IS NOT NULL
AND t1.val1 IS NOT NULL
GROUP BY 1, 2, t1.val2
ORDER BY t1.id
;
SQLFiddle link
【讨论】:
【参考方案2】:我不知道你会如何变得更好。您提到 val2 值是连续的,但您的解决方案所需要的只是它们正在增加。 COALESCE
并不是真正需要的。我发现这个版本更容易阅读......而且它可能会越来越快,因为不需要在 val2 上分组。但这不是根本性的变化。
SELECT
t1.id
, t1.val1
, min(t2.val2)
FROM test t1
LEFT OUTER JOIN test t2 on (t1.id <= t2.id and t2.val2 is not null)
GROUP BY t1.id, t1.val1
ORDER BY t1.id
;
【讨论】:
【参考方案3】:这适用于val2
中的任何 值。它们不需要是连续的,NULL
值可以出现在任何地方(包括最后一行)。
SELECT t1.id, t1.val1, COALESCE(t1.val2, t2.val2) as val2
FROM test t1
LEFT JOIN test t2
ON t2.id > t1.id
AND t1.val2 IS NULL
AND t2.val2 IS NOT NULL
AND NOT EXISTS (
SELECT 1
FROM test t3
WHERE t3.id > t1.id
AND t3.id < t2.id
AND t3.val2 IS NOT NULL
)
ORDER BY t1.id;
它还删除了查询中的一个极端情况错误:WHERE 子句将删除带有val2 IS NULL
的尾随行。您必须将该条件提升到 JOIN 子句中。详情:Query with LEFT JOIN not returning rows for count of 0
不确定它是否比 Redshift 中的 CROSS JOIN
/ min()
更快。
【讨论】:
【参考方案4】:您可以通过以下方式避免 JOIN 并使用窗口函数:
SELECT id, val1, val2,
COALESCE(val2, LEAD(val2, dist::int) OVER (ORDER BY id)) AS notNullVal2
FROM (
SELECT id, val1, val2, c,
ROW_NUMBER() OVER (PARTITION BY c ORDER BY id DESC) AS dist
FROM (
SELECT id, val1, val2,
COUNT(val2) OVER (ORDER BY id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS c
FROM test
)
)
ORDER BY id
【讨论】:
以上是关于用 Redshift 中的第一个非空跟随值填充缺失值的主要内容,如果未能解决你的问题,请参考以下文章
如何在 PySpark 中用该列的第一个非空值填充该列的空值