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
:
归档时间: |
|
查看次数: |
2992 次 |
最近记录: |