「標準」SQL
前の質問に投稿したものと同様に、再帰CTEはエレガントで、おそらく標準SQLでそれを行うための最速の方法です(特にユーザーあたりの行数が多い場合)。
WITH RECURSIVE t AS (
SELECT row_number() OVER (PARTITION BY usr ORDER BY id DESC) AS rn
,usr, cola, colb, colc
FROM tbl
)
, x AS (
SELECT rn, usr, cola, colb, colc
FROM t
WHERE rn = 1
UNION ALL
SELECT t.rn, t.usr
, COALESCE(x.cola, t.cola)
, COALESCE(x.colb, t.colb)
, COALESCE(x.colc, t.colc)
FROM x
JOIN t USING (usr)
WHERE t.rn = x.rn + 1
AND (x.cola IS NULL OR x.colb IS NULL OR x.colc IS NULL)
)
SELECT DISTINCT ON (usr)
usr, cola, colb, colc
FROM x
ORDER BY usr, rn DESC;
->要求されたPostgreSQLのsqlfiddle。
唯一の非標準要素はDISTINCT ON
、であり、これは標準の拡張DISTINCT
です。SELECT
標準SQLの場合、finalをこれに置き換えます。
SELECT usr
,max(cola) As cola
,max(colb) As colb
,max(colc) As colc
FROM x
GROUP BY usr
ORDER BY usr;
「標準SQL」の要求は、使用が制限されています。標準は紙にのみ存在します。RDBMSは100%標準SQLを実装していません。標準には無意味な部分が所々に含まれているため、それも無意味です。間違いなく、PostgreSQLの実装は標準に最も近いものの1つです。
PL/pgSQL関数
このソリューションはPostgreSQLに固有ですが、非常にうまく機能するはずです。
私は上のフィドルで示されているのと同じテーブルの上に構築しています。
CREATE OR REPLACE FUNCTION f_last_nonull_per_user()
RETURNS SETOF tbl AS
$func$
DECLARE
_row tbl; -- table name can be used as row type
_new tbl;
BEGIN
FOR _new IN
SELECT * FROM tbl ORDER BY usr, id DESC
LOOP
IF _new.usr = _row.usr THEN
_row.id := _new.id; -- copy only id
IF _row.cola IS NULL AND _new.cola IS NOT NULL THEN
_row.cola := _new.cola; END IF; -- only if no value found yet
IF _row.colb IS NULL AND _new.colb IS NOT NULL THEN
_row.colb := _new.colb; END IF;
IF _row.colc IS NULL AND _new.colc IS NOT NULL THEN
_row.colc := _new.colc; END IF;
ELSE
IF _new.usr <> _row.usr THEN -- doesn't fire on first row
RETURN NEXT _row;
END IF;
_row := _new; -- remember row for next iteration
END IF;
END LOOP;
RETURN NEXT _row; -- return row for last usr
END
$func$ LANGUAGE plpgsql;
電話:
SELECT * FROM f_last_nonull_per_user();
id
行全体を返します-すべての列を埋めるのに必要な最小値を含みます。