将两个事件表合并为一个时间表


12

给定两个表:

CREATE TABLE foo (ts timestamp, foo text);
CREATE TABLE bar (ts timestamp, bar text);

我想编写一个查询,对于回报值tsfoo以及bar代表最新的值的统一视图。换句话说,如果foo包含:

ts | foo
--------
1  | A
7  | B

bar包含:

ts | bar
--------
3  | C
5  | D
9  | E

我想要一个返回的查询:

ts | foo | bar
--------------
1  | A   | null
3  | A   | C
5  | A   | D
7  | B   | D
9  | B   | E

如果两个表同时具有一个事件,则顺序无关紧要。

我已经能够使用union all和dummy值创建所需的结构:

SELECT ts, foo, null as bar FROM foo
UNION ALL SELECT ts, null as foo, bar FROM bar

这将为我提供新值的线性时间线,但是我不太能够确定如何根据前几行填充空值。我已经试过了lagwindow函数,但AFAICT只会看前一行,而不会递归地向后看。我研究了递归CTE,但是我不确定如何设置开始条件和终止条件。


值是否随着时间的推移foobar严格上升,还是测试用例在这方面具有误导性?
Erwin Brandstetter 2015年

2
为了节省其他人的麻烦,sqlfiddle.com /#!15/511414
Craig Ringer

1
给出答案后,不要改变问题的性质,而要提出一个新问题。您可以随时链接到此参考。(如果有答案,您甚至可以提供自己的答案。)原始版本对于公众应该是有趣的。让我们在一个问题中不要太多。
Erwin Brandstetter

对不起,超载。我删除了后续步骤,并将其添加为新问题
Christopher Currie

Answers:


7

使用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;

由于count()不计算NULL值,因此它方便地仅随每个非null值而增加,从而形成将共享相同值的组。在外部SELECTmin()(或max())同样会忽略NULL值,从而每个组选择一个非空值。Voilá。

相关FULL JOIN案例:

这是程序解决方案可能更快的一种情况,因为它可以在一次扫描中完成工作。像这样的plpgsql函数

CREATE OR REPLACE FUNCTION f_merge_foobar()
  RETURNS TABLE(ts int, foo text, bar text) 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$ LANGUAGE plpgsql;

呼叫:

SELECT * FROM f_merge_foobar();

db <> 在这里摆弄,展示了两者。

相关答案说明#variable_conflict use_column


有趣的问题不是吗。我认为有效的解决方案可能需要创建类似coalesce窗口的函数。
Craig Ringer

@CraigRinger:的确如此。我发现自己希望,想知道,在想..这种方式应该可以在没有子查询的情况下实现,但是我没有找到方法。在这种情况下,plpgsql函数会更快,因为它可以扫描每个表一次。
Erwin Brandstetter,2015年

@Christopher:我会对设置中每个变体的性能感兴趣。EXPLAIN ANALYZE,最好的5 ...?
Erwin Brandstetter 2015年

2
可惜Postgres尚未实现IGNORE NULLS(如Oracle那样:sqlfiddle.com /#!4 / fab35/1)。
ypercubeᵀᴹ

1
@ypercube:是的,Oracle simple根本不存储NULL值,因此无法分辨出''和NULL 之间的区别。
Erwin Brandstetter 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.