FROM句とWHERE句
SQLが分からないという人は、文法を理解しようとしてしまっていることが多い。ですが、私は文法解説は極力やりません。SQLは全体を把握してイメージでとらえないとまっとうに書けないからです。
SQLのSELECTは次の順で処理されます。
FROM・WHERE句の中のサブクエリ
FROM・WHERE句(WHERE句の中の相関サブクエリ)
GROUP BY句
HAVING句の中のサブクエリ
HAVING句(HAVING句の中の相関サブクエリ)
SELECT句
ORDER BY句
SQLが苦手だという人のほとんどは、最初に処理されるFROM句・WHERE句が理解できていません。SQLの基礎はFROM句とWHERE句で、これが理解できるだけで7〜8割できたのも同じですので、しっかりと身につけてください。
まずはイメージから
TABLE_A と TABLE_B をそれぞれ B_ID と ID で結合するとすると、結合する種類によって、青色の部分が抽出されます。
一致するもののみを抽出するイメージです。
あるいは、内側だけ(INNER)とイメージしてください。
左側に記述したテーブルのデータをすべて抽出するイメージです。
あるいは、左側は外側も(LEFT OUTER)とイメージしてください。
右側に記述したテーブルのデータをすべて抽出するイメージです。
あるいは、右側は外側も(RIGHT OUTER)とイメージしてください。
■図4 FULL OUTER JOINのイメージ
結合によっての抽出は行わないイメージです。
あるいは、すべて外側も(FULL OUTER)とイメージしてください。
左右を間違えない。
JOINを書くとき、左右を逆に書く人がいます。
FROM
TABLE_A AS a
LEFT JOIN TABLE_B AS b
ON b.ID = a.B_ID
しかし、SQL文は本来は1文になりますので1行で書くと、
FROM TABLE_A AS a LEFT JOIN TABLE_B AS b ON b.ID = a.B_ID
結合条件で逆になるのは、意味が分かってない証拠です。
抽出条件と結合条件
よく間違えられるのは、抽出条件と結合条件をごっちゃにしているものです。
例えば、TABLE_B は論理削除することになっていて、「削除FLG」という項目があって、図2の通りデータを抽出したいとします。
FROM
TABLE_A AS a
LEFT JOIN TABLE_B AS b
ON a.B_ID = b.ID
WHERE
b.削除FLG = 0
図の通り、紫の部分は
「b.削除FLG が NULL」
に設定されているため、
「NULL = 0」つまり、紫の部分はすべてFALSEになるため、抽出される結果は 図1(INNER JOIN)と同じになってしまいます。
これは非常によく見られる間違いです。
これを防ぐために
FROM
TABLE_A AS a
LEFT JOIN
(SELECT * FROM TABLE_B WHERE 削除FLG = 0) AS b
ON a.B_ID = b.ID
とサブクエリにする人もいますけれど、サブクエリとのJOINではインデックスが使えませんので、TABLE_B.ID にインデックスがあっても利用できなくなり、非常に遅いクエリになります。
正しくは、
FROM
TABLE_A AS a
LEFT JOIN TABLE_B AS b
ON a.B_ID = b.ID
AND 0 = b.削除FLG
となります。
このとき、
FROM
TABLE_A AS a
LEFT JOIN TABLE_B AS b
ON a.B_ID = b.ID
AND b.削除FLG = 0
でも、同じ結果になりますが(RDBMSのバージョンによってはならないこともある)*1先ほども書いたとおり、テーブルの左右は合わせるべきです。イメージとしては、
「TABLE_Aにある 0 というリテラルと結合する」
という、あくまでも「結合条件だ」というイメージで書きましょう。逆に書く人が多いのですけれど、逆に書いている人は
「b.削除FLG = 0」は抽出条件だけどちゃんと動いてくれないからFROM句に書く
というイメージを持っていると思います。この感覚の違いがSQL文が複雑になると書けなくなる原因の一つになります。
SQLを書くときは、必ず、抽出条件か、結合条件か、イメージしながら書きましょう。
*1:私の記憶では、Oracle9.1.4はNGで、Oracle9.1.6ではちゃんと出た。リビジョン違いと言うことはバグ扱いかな?