web-dev-qa-db-ja.com

左外部結合は結合的ですか?

左外部結合が可換でない理由は簡単に理解できますが、それらが結合的であるかどうかを理解するのに問題があります。いくつかのオンラインソースはそうではないと示唆していますが、私はこれが事実であると自分自身に納得させることができませんでした。

A、B、Cの3つのテーブルがあるとします。

AにIDとB_IDの2つの列が含まれているとします。ここで、IDはテーブルAの主キーであり、B_IDはテーブルBの主キーに対応する外部キーです。

BにIDとC_IDの2つの列が含まれているとします。ここで、IDはテーブルBの主キーであり、C_IDはテーブルCの主キーに対応する外部キーです。

CにIDとVALUEの2つの列が含まれているとします。ここで、IDはテーブルCの主キーであり、VALUEには任意の値が含まれています。

では、_(A left outer join B) left outer join C_はA left outer join (B left outer join C)と等しくてはいけませんか?

24
Tianxiang Xiong

あなたの質問が示唆しているように、あなたが外部キーに参加していると仮定しているなら、そうです、そうです、 Przemyslaw Kruglejの答え でカバーされているように、OUTERJOINは連想的であることが保証されていると思います。

ただし、実際にJOIN条件を指定していないことを考えると、根本的に正しい答えは、いいえ、それらが関連付けられるとは限らないということです。ひねくれたON句との結合性に違反する簡単な方法は2つあります。

1. JOIN条件の1つには、3つのテーブルすべての列が含まれます

これは結合性を侵害するためのかなり安価な方法ですが、厳密に言えば、あなたの質問ではそれを禁じるものは何もありません。質問で提案された列名を使用して、次の2つのクエリを検討してください。

-- This is legal
SELECT * FROM (A JOIN B ON A.b_id = B.id) 
              JOIN C ON (A.id = B.id) AND (B.id = C.id)


-- This is not legal
SELECT * FROM A
              JOIN (B JOIN C ON (A.id = B.id) AND (B.id = C.id))
              ON A.b_id = B.id

下のクエリは有効なクエリでもありませんが、上のクエリは有効です。明らかに、これは結合性に違反します。

2. 1つのテーブルのすべてのフィールドがNULLであるにもかかわらず、JOIN条件の1つを満たすことができます。

このようにして、JOINの順序に応じて、結果セットに異なる数の行を含めることもできます。たとえば、BでAを結合する条件をA.b_id = B.idとしますが、CでBを結合する条件をB.id IS NULLとします。

したがって、出力が大きく異なる次の2つのクエリを取得します。

SELECT * FROM (A LEFT OUTER JOIN B ON A.b_id = B.id) 
              LEFT OUTER JOIN C ON B.id IS NULL;


SELECT * FROM A 
              LEFT OUTER JOIN (B LEFT OUTER JOIN C ON B.id IS NULL)
              ON A.b_id = B.id;

ここで実際の動作を確認できます: http://sqlfiddle.com/#!9/d59139/1

16
Mark Amery

このスレッドでは、それらは結合的ではないと言われています: LEFT OUTER JOINは結合的ですか?

ただし、オンラインで、左端と右端のテーブルに共通の属性がない場合( ここ )、外部結合は結合的であると記載されている本を見つけました。

グラフィカルなプレゼンテーション(MSPaint ftw)は次のとおりです。

Graphical presentation of impact of left outer joins order

それを見る別の方法:

テーブルAがBと結合し、BがCと結合すると言ったので、次のようになります。

  • 最初にAとBを結合すると、Aからのすべてのレコードが残ります。それらの一部にはBからの値があります。ここで、これらの行のsomeについてBから値を取得した場合、Cから値を取得します。
  • 最初にBとCを結合すると、テーブルB全体が表示されます。ここで、一部のレコードにはCの値があります。次に、Aからすべてのレコードを取得し、Bのすべての行をCに結合してそれらの一部を結合します。繰り返しますが、Aからすべての行を取得しますが、一部の行にはBの値があり、一部の行にはCの値があります。

あなたが説明した条件で、LEFT結合のシーケンスによってはデータが失われる可能性はないと思います。

Tilakが回答で提供したデータ(現在は削除されています)に基づいて、簡単なテストケースを作成しました。

CREATE TABLE atab (id NUMBER, val VARCHAR2(10));
CREATE TABLE btab (id NUMBER, val VARCHAR2(10));
CREATE TABLE ctab (id NUMBER, val VARCHAR2(10));

INSERT INTO atab VALUES (1, 'A1');
INSERT INTO atab VALUES (2, 'A2');
INSERT INTO atab VALUES (3, 'A3');

INSERT INTO btab VALUES (1, 'B1');
INSERT INTO btab VALUES (2, 'B2');
INSERT INTO btab VALUES (4, 'B4');

INSERT INTO ctab VALUES (1, 'C1');
INSERT INTO ctab VALUES (3, 'C3');
INSERT INTO ctab VALUES (5, 'C5');

SELECT ab.aid, ab.aval, ab.bval, c.val AS cval
  FROM (
    SELECT a.id AS aid, a.val AS aval, b.id AS bid, b.val AS bval
      FROM atab a LEFT OUTER JOIN btab b ON (a.id = b.id)
    ) ab
    LEFT OUTER JOIN ctab c ON (ab.bid = c.id)
ORDER BY ab.aid
;
       AID AVAL BVAL CVAL 
 ---------- ---------- ---------- ---------- 
 1 A1 B1 C1 
 2 A2 B2 
 3 A3 
SELECT a.id, a.val AS aval, bc.bval, bc.cval
  FROM
    atab a
    LEFT OUTER JOIN (
      SELECT b.id AS bid, b.val AS bval, c.id AS cid, c.val AS cval
        FROM btab b LEFT OUTER JOIN ctab c ON (b.id = c.id)
    ) bc
      ON (a.id = bc.bid)
ORDER BY a.id
;
        ID AVAL BVAL CVAL 
 ---------- ---------- ---------- ---------- 
 1 A1 B1 C1 
 2 A2 B2 
 3 A3 

この特定の例では、両方のソリューションで同じ結果が得られるようです。これらのクエリが異なる結果を返すようにする他のデータセットは考えられません。

SQLFiddleで確認してください:

24

以前の回答に加えて:このトピックは、Michael M. David、Advanced ANSI SQL Data Modeling and Structure Processing、Artech House、1999年、19〜21ページでうまく説明されています。利用可能なページ オンライン

テーブル(LEFT JOIN ...)と結合句(ON ...)を別々に検討する必要があるため、結合性は両方(テーブル句の再配置と再配置)を参照できると彼が説明していることは特に注目に値します。結合条件、つまり節)。したがって、結合性の概念は、たとえば数値の加算の場合と同じではなく、2つの次元があります。

4
Fabian