看起来您必须使用普通 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
。如果您能回来比较解决方案,那就太好了。