Chr*_*rie 12 postgresql join window-functions
给定两个表:
CREATE TABLE foo (ts timestamp, foo text);
CREATE TABLE bar (ts timestamp, bar text);
Run Code Online (Sandbox Code Playgroud)
我想编写一个查询,对于回报值ts,foo以及bar代表最新的值的统一视图。换句话说,如果foo包含:
ts | foo
--------
1 | A
7 | B
Run Code Online (Sandbox Code Playgroud)
并bar包含:
ts | bar
--------
3 | C
5 | D
9 | E
Run Code Online (Sandbox Code Playgroud)
我想要一个返回的查询:
ts | foo | bar
--------------
1 | A | null
3 | A | C
5 | A | D
7 | B | D
9 | B | E
Run Code Online (Sandbox Code Playgroud)
如果两个表同时有一个事件,则顺序无关紧要。
我已经能够使用 union all 和 dummy 值创建所需的结构:
SELECT ts, foo, null as bar FROM foo
UNION ALL SELECT ts, null as foo, bar FROM bar
Run Code Online (Sandbox Code Playgroud)
这将为我提供新值的线性时间线,但我不太能弄清楚如何根据前一行填充空值。我已经尝试过lag窗口函数,但 AFAICT 它只会查看前一行,而不是递归地向后查看。我看过递归 CTE,但我不太确定如何设置开始和终止条件。
使用a FULL [OUTER] JOIN,结合两轮窗口函数:
SELECT ts
, min(foo) OVER (PARTITION BY foo_grp) AS foo
, min(bar) OVER (PARTITION BY bar_grp) AS bar
FROM (
SELECT ts, f.foo, b.bar
, count(f.foo) OVER (ORDER BY ts) AS foo_grp
, count(b.bar) OVER (ORDER BY ts) AS bar_grp
FROM foo f
FULL JOIN bar b USING (ts)
) sub;
Run Code Online (Sandbox Code Playgroud)
由于count()不计算 NULL 值,它方便地只随每个非空值增加,从而形成共享相同值的组。在外部SELECT, min()(or max()) 同样忽略 NULL 值,从而为每组选择一个非空值。瞧。
相关FULL JOIN案例:
这是程序解决方案可能更快的情况之一,因为它可以在一次扫描中完成工作。像这个plpgsql 函数:
CREATE OR REPLACE FUNCTION f_merge_foobar()
RETURNS TABLE(ts int, foo text, bar text)
LANGUAGE plpgsql AS
$func$
#variable_conflict use_column
DECLARE
last_foo text;
last_bar text;
BEGIN
FOR ts, foo, bar IN
SELECT ts, f.foo, b.bar
FROM foo f
FULL JOIN bar b USING (ts)
ORDER BY 1
LOOP
IF foo IS NULL THEN foo := last_foo;
ELSE last_foo := foo;
END IF;
IF bar IS NULL THEN bar := last_bar;
ELSE last_bar := bar;
END IF;
RETURN NEXT;
END LOOP;
END
$func$;
Run Code Online (Sandbox Code Playgroud)
称呼:
SELECT * FROM f_merge_foobar();
Run Code Online (Sandbox Code Playgroud)
db<>fiddle here,展示了两者。
旧的sqlfiddle。
相关答案解释了#variable_conflict use_column: