使用单个查询选择每列的最后一个值

时间:2012-11-12 16:25:59

标签: sql null

拥有以下数据(空白表示NULL):

ID    ColA    ColB    ColC
1     15              20
2     11      4       
3             3

如何在单个查询中获取每列的最后一个非NULL值?因此,给定数据的结果将是:

ColA    ColB    ColC
11      3       20

我找不到多少,似乎与我所描述的类似的函数是COALESCE,但在我的情况下它没有按预期工作。

2 个答案:

答案 0 :(得分:2)

看起来您必须使用纯SQL为每列运行单独的查询。对于一个小表,只有3列,@Guffa's query应该没问题。

3个窗口函数

您可以在一个查询中使用三个窗口函数执行相同操作:不确定这是否比三个单独的子查询更快:

SELECT first_value(cola) OVER (ORDER BY cola IS NULL, id DESC) AS cola
      ,first_value(colb) OVER (ORDER BY colb IS NULL, id DESC) AS colb
      ,first_value(colc) OVER (ORDER BY colc IS NULL, id DESC) AS colc
FROM   tbl
LIMIT  1;

count()作为窗口函数

您还可以利用count()不计算NULL值的事实。

WITH x AS (
   SELECT CASE WHEN count(cola) OVER w = 1 THEN cola ELSE NULL END AS cola
         ,CASE WHEN count(colb) OVER w = 1 THEN colb ELSE NULL END AS colb
         ,CASE WHEN count(colc) OVER w = 1 THEN colc ELSE NULL END AS colc
   FROM   tbl
   -- WHERE id > x -- safe to ignore a certain portion from a large table?
   WINDOW w AS (ORDER  BY id DESC)
   )
SELECT max(cola) AS cola, max(colb) AS colb, max(colc) AS colc
FROM   x

对于更大的表格和更多列,recursive CTE或程序函数将相当更快

递归CTE

WITH RECURSIVE x AS (
   SELECT cola, colb, colc
         ,row_number() OVER (ORDER  BY id DESC) AS rn
   FROM   tbl
   )

   , y AS (
   SELECT rn, cola, colb, colc
   FROM   x
   WHERE  rn = 1

   UNION ALL
   SELECT x.rn
        , COALESCE(y.cola,x.cola)
        , COALESCE(y.colb,x.colb)
        , COALESCE(y.colc,x.colc)
   FROM   y
   JOIN   x ON  x.rn = y.rn + 1
   WHERE  y.cola IS NULL OR y.colb IS NULL OR y.colc IS NULL
   )
SELECT cola, colb, colc
FROM   y
ORDER  BY rn DESC
LIMIT  1;

PL / pgSQL函数

为了获得最佳效果,我的钱就在这个上面了:

CREATE OR REPLACE FUNCTION f_last_nonull(OUT cola int
                                       , OUT colb int
                                       , OUT colc int) AS
$func$
DECLARE
   r record;
BEGIN

FOR r IN
   SELECT t.cola, t.colb, t.colc 
   FROM   tbl t
   ORDER  BY t.id DESC
LOOP
   IF cola IS NULL AND r.cola IS NOT NULL THEN cola := r.cola; END IF;
   IF colb IS NULL AND r.colb IS NOT NULL THEN colb := r.colb; END IF;
   IF colc IS NULL AND r.colc IS NOT NULL THEN colc := r.colc; END IF;

   EXIT WHEN NOT (cola IS NULL OR colb IS NULL OR colc IS NULL);
END LOOP;

END
$func$ LANGUAGE plpgsql;

呼叫:

SELECT * FROM f_last_nonull();
cola | colb | colc
-----+------+------
 11  | 3    | 20

使用EXPLAIN ANALYZE进行测试。如果你能回来对比解决方案,那就太好了。

答案 1 :(得分:1)

您可以使用子查询:

select
  (select ColA from TheTable where ColA is not null order by ID desc limit 1) as ColA,
  (select ColB from TheTable where ColB is not null order by ID desc limit 1) as ColB,
  (select ColC from TheTable where ColC is not null order by ID desc limit 1) as Col