SQLの話です。
以下のようなテーブルがあったとします。
■table_header(親テーブル)
(PK) | mng_key |
… |
■table_detail(子テーブル)
(PK) | mng_key |
(PK) | sub_key |
… |
ヘッダ情報を持つテーブルと明細情報を持つテーブルです。この2つは1:Nの関係を持っています。
table_headerには共通の情報を、table_detailには明細ごとの情報をそれぞれ持っています。
ここで、親と子の情報をひとまとめで取得したい場合、表結合を行います。
表結合には、パッと思いつくもので2通りの方法があります。
WHERE句でキーを結合
SELECT *
FROM table_header hed
, table_detail dtl
WHERE hed.mng_key = dtl.mng_key
INNER JOIN句でテーブルを結合
SELECT *
FROM table_header hed
INNER JOIN table_detail dtl
ON hed.mng_key = dtl.mng_key
今までこの2つの方法はそんなに違わないと思っていましたが、今日、初めてその違いを意識しました。
具体的には、2つのテーブルと紐付く3つ目のテーブルが登場した場合です。
■table_header(親テーブル)
(PK) | mng_key |
cmn_a | |
… |
■table_detail(子テーブル)
(PK) | mng_key |
(PK) | sub_key |
cmn_b | |
… |
■table_cmn
(PK) | surrogate_key |
cmn_a | |
cmn_b | |
… |
こんな感じの何かを管理しているtable_cmnも結合したい場合です。
通常であれば、このテーブルも結合するだけでよいのですが、問題はtable_cmnにはレコードが存在しない可能性もある場合です。
table_cmnに結合するべきレコードが存在しない場合、上記2つのSQLのように内部結合を行ってしまうと、table_cmnにつられてSELECT結果から除外されてしまいます。
そこで、table_cmnに関しては外部結合を行いたいです。ところが、上記2つのSQLでは結果が変わります。
WHERE句でキーを結合
SELECT *
FROM table_header hed
, table_detail dtl
, table_cmn cmn
WHERE hed.mng_key = dtl.mng_key
AND hed.cmn_a = cmn.cmn_a (+)
AND dtl.cmn_b = cmn.cmn_b (+)
INNER JOIN句でテーブルを結合
SELECT *
FROM table_header hed
INNER JOIN table_detail dtl
ON hed.mng_key = dtl.mng_key
LEFT OUTER JOIN table_cmn cmn
ON hed.cmn_a = cmn.cmn_a
AND dtl.cmn_b = cmn.cmn_b
この場合、前者ではORACLEエラーが発生します。これは(+)による結合をLEFT OUTER JOINの形式に変更しても同様です。
上記のSQLだと「ORA-01417」、LEFT OUTER JOINに変更した場合は左辺のテーブルのみ参照可能でtable_headerまで参照できないため「ORA-00904」、INNER JOIN+(+)の結合では「ORA-25156」となりました。
FROM句にテーブル名を列挙した後、WHERE句で結合条件を記述する場合と、FROM句でINNER JOINを用いてテーブル結合を行うのとでは、内部処理で結合される順番が異なるとかそんなんじゃないでしょうか。
他にもいろいろ書こうと思いましたが、また後日。