VillageSQL is a drop-in replacement for MySQL with extensions.
All examples on this page work on VillageSQL. Install Now →
EXISTS は「このサブクエリは何かを見つけたか」という、はいかいいえの質問をします。
サブクエリが何を返したかは気にせず、行があったかどうかだけを見ます。そのため、
関連する行の有無をテストしたいが、その列は 1 つも要らないときに適した道具です。
始める前に mysql -u root -p sakila でクライアントを開いてください。
EXISTS の仕組み
COLUMN_LIST を必要な列に、TABLE_NAME と OTHER_TABLE を 2 つのテーブルに、
OUTER_ALIAS と INNER_ALIAS をそれらの名前に、COLUMN_NAME をそれらを結ぶ列に、
内側の WHERE を行が関連していると言える条件に置き換えます。
SELECT 1 は慣習です。選択リストは無視されるため、SELECT 1、SELECT *、
および SELECT column はまったく同じように振る舞い、1 は何も読んでいないことを
はっきり示します。
このサブクエリは相関しています。外側の行を参照しているからです。1 行あれば
「はい」と答えるのに十分なので、サーバーは最初に一致した行で止まれます。
例
- 一致を持つ行
- 一致を持たない行
NOT EXISTS は対になるもう一方で、アンチ結合を書く方法です。
WHERE f.film_id IS NULL で答え、2 つの形は同じ行を返します。
NOT EXISTS は NOT IN が安全でない場面で安全
IN のレッスンでは、リストに NULL が含まれるとNOT IN が行を
まったく返さなくなることを示しています。NOT EXISTS にそのような罠はありません。
値を比較することがなく、行を数えるからです。
そのため、サブクエリが NULL になり得る列を読むときは NOT EXISTS を使って
ください。NOT IN は NOT NULL と宣言された列に対しては問題ありませんが、それが
成り立たなくなった瞬間にリスクになります。
EXISTS は行を増やさない
複数の一致する行を持つテーブルへの結合は、INNER JOIN のレッスンが示すように、外側の行を一致ごとに繰り返します。EXISTS にはそれが
できません。はいかいいえで答えるため、一致がいくつあっても外側の各行は 1 回だけ
現れます。
EXISTS、IN、および結合はどれも「関連する行を持つ」を表現でき、どれが最速かは
データ次第です。
MySQL におけるサブクエリと JOIN の比較では、
実行計画を使ってそれらを比較しています。
まとめ
- サブクエリが行を見つけるかどうかをテストするには
EXISTSを使います。 - 選択リストは無視されるため、内側には
SELECT 1と書きます。 - 一致のない行には
NOT EXISTSを使います。 - サブクエリの列が NULL になり得るときは、
NOT INよりNOT EXISTSを使います。 - 結合なら起きる行の増加は起きないと考えてください。
関連項目
- 相関サブクエリ — EXISTS が使うサブクエリの形
- IN — NULL の罠を持つ演算子
- LEFT JOIN — 同じ質問を結合で問うたもの
- MySQL におけるサブクエリと JOIN の比較 — オプティマイザがどの形を好むか