具有以下数据(空白表示空):
ID ColA ColB ColC
1 15 20
2 11 4
3 3
如何在单个查询中获取每列的最后一个非 NULL 值?因此,给定数据的结果将是:
ColA ColB ColC
11 3 20
我没有找到太多,似乎与我描述的类似功能的函数是COALESCE
,但它在我的情况下没有按预期工作。
看起来您必须使用纯 SQL 对每列运行单独的查询。对于只有 3 列的小表,@Guffa的查询应该没问题。
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
对于更大的表和更多的列,递归 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
进行测试。如果您能回来比较解决方案,那就太好了。
您可以使用子查询:
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