8

データベース値の変更を追跡する2つの履歴テーブルがあり、リビジョンIDを使用して個々の変更を追跡しています。例えば

表1:

 rev |  A   |  B 
=================
 1   |  100 | 'A'
 4   |  150 | 'A'
 7   |  100 | 'Z'

表2:

 rev |  C   |  D 
==================
 1   |  200 | True
 5   |    0 | True
 8   |    0 | False

目標は、2つのテーブルを次のようにマージすることです。

 rev |  A   |  B  |  C  |  D 
===============================
 1   |  100 | 'A' | 200 | True
 4   |  150 | 'A' | 200 | True
 5   |  150 | 'A' |   0 | True
 7   |  100 | 'Z' |   0 | True
 8   |  100 | 'Z' |   0 | False

特定のリビジョンについて、そのリビジョンまたはそれよりも小さい最高のリビジョンに対応する値を使用するという考え方です。

頭に浮かぶSQLクエリは、制約rev1 <rev2で2つのテーブルをクロス結合し、サブクエリを使用して行を選択するようなものです。ここで、指定されたrev2ごとにrev1 = max(rev1)です。このクエリを、rev2とrev1を交換する対応するクエリと結合ます最後に、 rev1=rev2の場所から重複を除外します。

質問は次のとおりです。

  • このタイプの結合の名前はありますか?
  • SQLでこのタイプの結合を実行するためのイディオムはありますか、それともプログラムで実行する方がよいでしょうか(これは間違いなくはるかに単純で、おそらくより効率的です)。
4

3 に答える 3

2

SQL フィドル

select
    coalesce(t1.rev, t2.rev) rev,
    coalesce(a, lag(a, 1) over(order by coalesce(t2.rev, t1.rev))) a,
    coalesce(b, lag(b, 1) over(order by coalesce(t2.rev, t1.rev))) b,
    coalesce(c, lag(c, 1) over(order by coalesce(t1.rev, t2.rev))) c,
    coalesce(d, lag(d, 1) over(order by coalesce(t1.rev, t2.rev))) d
from
    t1
    full join
    t2 on t1.rev = t2.rev
order by rev
于 2012-10-25T20:26:11.300 に答える
1

これは、サブクエリによって実現できます

SELECT ISNULL(Table1.rev,Table2.rev) AS rev
,ISNULL(A,(SELECT TOP 1 A FROM Table1 AS T1 WHERE ISNULL(Table1.rev,Table2.rev) > T1.rev AND A IS NOT NULL ORDER BY rev DESC)) AS A
,ISNULL(B,(SELECT TOP 1 B FROM Table1 AS T1 WHERE ISNULL(Table1.rev,Table2.rev) > T1.rev AND B IS NOT NULL ORDER BY rev DESC)) AS B
,ISNULL(C,(SELECT TOP 1 C FROM Table2 AS T2 WHERE ISNULL(Table1.rev,Table2.rev) > T2.rev AND C IS NOT NULL ORDER BY rev DESC)) AS C
,ISNULL(D,(SELECT TOP 1 D FROM Table2 AS T2 WHERE ISNULL(Table1.rev,Table2.rev) > T2.rev AND D IS NOT NULL ORDER BY rev DESC)) AS D
FROM Table1
FULL OUTER JOIN Table2
ON Table1.rev = Table2.rev
于 2012-10-23T07:54:04.197 に答える
0

そのようなクエリを処理するための特定の結合タイプはありません。複雑なクエリとして、またはプログラムで行う必要があります。以下は、サンプルデータを使用した、この問題の PL/PGSQL コードの例です。

CREATE OR REPLACE FUNCTION getRev(OUT rev INT, OUT A INT, OUT B CHAR, OUT C INT, OUT D BOOL) RETURNS SETOF record STABLE AS
$BODY$
DECLARE
    c1 SCROLL CURSOR FOR SELECT * FROM Table1 ORDER BY rev;
    c2 SCROLL CURSOR FOR SELECT * FROM Table2 ORDER BY rev;
    r1    Table1%ROWTYPE;
    r1c   Table1%ROWTYPE;
    r2    Table2%ROWTYPE;
    r2c   Table2%ROWTYPE;
BEGIN
  OPEN c1;
  OPEN c2;
  FETCH c1 INTO r1;
  FETCH c2 INTO r2;
  r1c := r1;
  r2c := r2;
  WHILE r1 IS NOT NULL AND r2 IS NOT NULL
  LOOP
    CASE 
    WHEN r1.rev = r2.rev THEN 
      rev := r1.rev;
      A := r1.a;
      B := r1.b;
      C := r2.c;
      D := r2.d;
      FETCH c1 INTO r1c;
      FETCH c2 INTO r2c;
      CASE 
        WHEN r1c.rev = r2c.rev THEN
      r1 := r1c;
      r2 := r2c;
        WHEN r1c.rev < r2c.rev THEN
          r1 := r1c;
      FETCH PRIOR FROM c2 INTO r2c;
    ELSE
          r2 := r2c;
      FETCH PRIOR FROM c1 INTO r1c;
      END CASE;
    WHEN r1.rev < r2.rev THEN
      WHILE r1c IS NOT NULL AND r1c.rev < r2.rev LOOP
         r1 := r1c;
         FETCH c1 INTO r1c;
      END LOOP;
      rev := r2.rev;
      A := r1.a;
      B := r1.b;
      C := r2.c;
      D := r2.d;
      r1 := r1c;
    ELSE 
      WHILE r2c IS NOT NULL AND r2c.rev < r1.rev LOOP
         r2 := r2c;
         FETCH c2 INTO r2c;
      END LOOP;
      rev := r1.rev;
      A := r1.a;
      B := r1.b;
      C := r2.c;
      D := r2.d;
      r2 := r2c;
    END CASE;
    RETURN NEXT;
  END LOOP;
  CLOSE c1;
  CLOSE c2;
  RETURN;
END
$BODY$
LANGUAGE 'plpgsql';

これは、O (長さ (テーブル 1) + 長さ (テーブル 2)) で実行する必要があります。

「CASE WHEN r1.rev = r2.rev」のトリッキーな部分に注意してください。次の反復のためにスキャンを続行するテーブルを選択する必要があります。両方のテーブルで利用可能なすべての rev 番号を取得するために、正しいものは、カーソルの後に最小の rev 値を持つものです。C または C++ でコーディングすると、確実にパフォーマンスが向上します。

于 2012-10-25T16:52:46.380 に答える