外部結合
NATURAL JOIN
やJOIN USING
で求める等価結合は、ON
句とイコール(=
)演算子を使用しても同じ結果を得られます。
ON
句は、互いのレコードにおいて結合列名が異なる場合や、等価以外(例えば、BETWEEN AND
)の演算子を使用した結合条件を指定する場合(非等価結合)に使用できます。
単なるJOIN
(またはINNER JOIN
)は、結合条件に一致したレコードを結果として出力しますが、JOIN
の前にLEFT
、RIGHT
、FULL
を指定すると、結合条件を満たさないレコードも出力できます。
次の例題を見てください。
問2
次の2つの表を確認してください。
SQL> desc promotions 名前 NULL? 型 ----------------------------- -------- -------------------- PROMO_ID NOT NULL NUMBER(6) PROMO_NAME NOT NULL VARCHAR2(30) PROMO_SUBCATEGORY NOT NULL VARCHAR2(30) PROMO_SUBCATEGORY_ID NOT NULL NUMBER PROMO_CATEGORY NOT NULL VARCHAR2(30) PROMO_CATEGORY_ID NOT NULL NUMBER PROMO_COST NOT NULL NUMBER(10,2) PROMO_BEGIN_DATE NOT NULL DATE PROMO_END_DATE NOT NULL DATE PROMO_TOTAL NOT NULL VARCHAR2(15) PROMO_TOTAL_ID NOT NULL NUMBER SQL> desc sales 名前 NULL? 型 ----------------------------- -------- -------------------- PROD_ID NOT NULL NUMBER CUST_ID NOT NULL NUMBER TIME_ID NOT NULL DATE CHANNEL_ID NOT NULL NUMBER PROMO_ID NOT NULL NUMBER QUANTITY_SOLD NOT NULL NUMBER(10,2) AMOUNT_SOLD NOT NULL NUMBER(10,2)
次の問い合わせを実行しました。
SELECT DISTINCT pr.promo_id,pr.promo_name,s.prod_id FROM sales s RIGHT OUTER JOIN promotions pr ON s.promo_id = pr.promo_id WHERE s.prod_id IS NULL;
この問い合わせに対して、正しい記述を選びなさい。
- a. 外部結合にDISTINCTは使用できないので、エラーになる
- b. 売り上げがあったプロモーションの名前と製品のIDが出力される
- c. 売り上げがなかったプロモーションの名前と製品のIDが出力される
- d. 売り上げの有無に関係なく、すべてのプロモーションの名前と製品のIDが出力される
- e. プロモーションの有無に関係なく、売り上げのあったプロモーションの名前と製品のIDが出力される
結合では、結合条件を満たすレコードが出力されるわけですから、一方の表にしか存在しない結合列値を持つレコードは出力されません。設問のプロモーション(promotions)表と売り上げ(sales)表を使用して説明するならば、「プロモーションは行ったが売り上げにはつながらなかった製品」が存在する場合、そのプロモーションは結合結果には出力されません。また、「プロモーションが行われずして、販売された(売りあがった)製品」が存在しても、やはり結合結果には出力されません。 プロモーションが行われた上で、販売された製品のIDおよびプロモーションの名前が出力されます。
しかし、実務においては、「お金をかけたのに結果が出てない(売り上がっていない)プロモーションはどれだ?」とか「プロモーションなんてしなくても売れている製品はどれだ?」ということに関心が向く場合があります。 こうしたシーンでは外部結合を使用します。外部結合にすれば、「プロモーションした製品が売れた」という結果と共に、これらのレコードも出力されます。
外部結合にはLEFT
、RIGHT
、FULL
の指定ができました。
FROM 表1 {LEFT | RIGHT | FULL} OUTER JOIN 表2
LEFT
とRIGHT
は、JOIN
句の位置から見て左(LEFT
)か右(RIGHT
)と覚えてください。
JOIN
句の左、つまりFROM
句に記述した表1のレコードを全部(結合条件を満たさないレコードも含め)出力したいならLEFT OUTER JOIN
と記述し、JOIN
句の右、つまりJOIN
句に記述した表2のレコードを全部出力したいならRIGHT
です。
FULL
は左(表1)も右(表2)も全部です。
これを踏まえて問2のSQLを見てみるとRIGHT OUTER
ですから、全部出したいのはJOIN
句の右に記述されているpromotionsです。よって、売り上げがあったことにこだわっている選択肢bと、プロモーションの有無に関係なく(つまり、製品を全部出したい)と言っている選択肢eは不正解です。
OUTER JOIN
しているだけなら、売り上げの有無に関係なくと言っている選択肢dが正解になるでしょうが、設問のSQLにはWHERE
句が記述されています。外部結合をすると、相手の表(sales)には一致するレコードがないわけですからNULL
が出力されます(実行例参照)。WHERE
句では、そのNULL
レコードを絞り込み条件(検索条件)にしているので、売り上げがなかったプロモーションを求めている選択肢cが正解です(選択肢dは不正解)。
SQL> SELECT DISTINCT pr.promo_id,pr.promo_name,s.prod_id 2 FROM sales s 3 RIGHT OUTER JOIN promotions pr 4 ON s.promo_id = pr.promo_id 5 WHERE s.prod_id IS NULL; PROMO_ID PROMO_NAME PROD_ID ---------- ------------------------------ ---------- 306 newspaper promotion #19-306 136 flyer promotion #24-136 345 newspaper promotion #16-345 (以下省略)
なお、実行例から分かるように、外部結合でもDISTINCT
は使用できるので、選択肢aは不正解です。