2つのイベントテーブルを1つのタイムラインにまとめる


12

2つのテーブルがある場合:

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

私はのための戻り値というクエリを書きたいtsfoobarその直近の値の統一見解を表しています。つまり、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とダミー値を使用して、必要な構造を作成できました。

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

これにより、新しい値の線形タイムラインが得られますが、前の行に基づいてnull値を設定する方法を見つけることはできません。lagウィンドウ関数を試しましたが、AFAICTは前の行のみを参照し、再帰的に後方を検索しません。再帰CTEを見てきましたが、開始条件と終了条件の設定方法がよくわかりません。


値は時間の経過fooとともにbar厳密に上昇していますか、またはこの点でテストケースが誤解を招いていますか?
アーウィンブランドステッター

2
誰手間、保存するにはsqlfiddle.com/#!15/511414
クレイグ・リンガー

1
回答が出された後に質問の性質を変更するのではなく、新しい質問をしてください。いつでも参照用にこれにリンクできます。(回答がある場合は、独自の回答を提供することもできます。)元のバージョンは、一般の人々にとって興味深いものでなければなりません。単一の質問で多くを詰め込まないようにしましょう。
アーウィンブランドステッター

過負荷でごめんなさい。フォローアップを削除し、新しい質問として追加しました
クリストファーカリー

回答:


7

を使用してFULL [OUTER] JOIN、2ラウンドのウィンドウ関数と組み合わせます

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())同様に、これによりグループごとに1つの非ヌル値をピッキング、NULL値を無視します。ボイラ。

関連FULL JOIN事例:

これは、1回のスキャンでジョブを完了できるため、手続き型ソリューションのほうが高速になる場合の1つです。この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 <> fiddle 、両方を示しています。

関連する回答#variable_conflict use_column


興味深い問題ではありません。効率的なソリューションには、おそらくcoalesce-likeウィンドウ関数の作成が必要だと思います。
クレイグリンガー

@CraigRinger:確かに。私は、これが何らかの方法でサブクエリなしで可能になるはずだと思って、疑問に思って、考えていますが、方法を見つけることができませんでした。plpgsql関数は、各テーブルを1回スキャンできるため、より高速になるケースの1つです。
アーウィンブランドステッター

@Christopher:セットアップの各バリアントのパフォーマンスに興味があります。EXPLAIN ANALYZE、ベスト5 ...?
アーウィンブランドステッター

2
残念ながら、Postgresはまだ実装していませんIGNORE NULLS(Oracleの場合:sqlfiddle.com / #!4 / fab35/1)。
ypercubeᵀᴹ

1
@ypercube:はい、Oracle simpleはNULL値をまったく保存しないため''、NULLとNULLの違いを判別できません。
アーウィンブランドステッター
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.