在Redshift中使用第一个非空的后续值填充缺失值

时间:2014-07-01 23:01:22

标签: sql amazon-redshift

我在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中实现此目的的最佳方法是什么?

4 个答案:

答案 0 :(得分: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

答案 1 :(得分:0)

我不知道你会怎样变得更好。你提到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
;

答案 2 :(得分:0)

这适用于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()更快。

答案 3 :(得分:0)

您可以使用以下内容避免使用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