VillageSQL is a drop-in replacement for MySQL with extensions.
All examples on this page work on VillageSQL. Install Now →
サブクエリの種類
非相関サブクエリは一度だけ実行され、その結果が外側のクエリで使われます。EXPLAIN で確認してください。
IN と EXISTS と JOIN の比較
この 3 つのパターンは、多くの場合同じ問題を解決します。
MySQL のオプティマイザは
IN (subquery) を内部的にセミジョインに書き換えることが多いため、パフォーマンスの差は無視できることがよくあります。サブクエリの結果セットが大きい場合は、EXISTS または JOIN のほうが信頼できます。
NOT IN の NULL 問題
NOT IN は、サブクエリが 1 つでも NULL を返すと予想外の動作をします。
orders.customer_id に NULL の行が 1 つでもあると、NOT IN 全体が空を返します。これは正しい SQL の動作です。NULL は不明なので、NOT IN (NULL, 1, 2) はすべての比較に対して UNKNOWN になります。代わりに NOT EXISTS を使ってください。
FROM 内のサブクエリ(派生テーブル)
FROM 句のサブクエリは派生テーブルを作ります。
パフォーマンスに関する考慮事項
MySQL のオプティマイザは、多くのサブクエリのパターンを書き換えます。手動で最適化する前に、次のことを行ってください。- サブクエリ版と JOIN 版の両方に
EXPLAINを実行し、MySQL が実際に何を実行しているかを確認する - 結合列や相関列にインデックスがあることを確認する。通常はここがボトルネックになります
EXPLAINが実行計画に大きな差を示す場合にのみ書き換える
よくある質問
JOIN は常にサブクエリより速いですか?
必ずしもそうではありません。MySQL 8.0 のオプティマイザは、多くの場合IN (subquery) をセミジョインに書き換え、明示的な JOIN と同じ実行計画を生成します。一方が常に速いと決めつけず、EXPLAIN で測定してください。
IN の代わりに EXISTS を使うべきなのはいつですか?
一致が存在するかどうかだけが重要な場合(一致したデータの内容は不要)、サブクエリが NULL を返す可能性がある場合、またはサブクエリが大きな結果セットを返す場合にEXISTS を使います。EXISTS は最初の一致で短絡しますが、IN は完全なリストを構築する必要があります。
セミジョインとは何ですか?
セミジョインは、右側に少なくとも 1 つの一致する行がある左側のテーブルの行を返します。複数の一致があっても、左側のテーブルの行を重複して生成しません。MySQL のオプティマイザは、IN (subquery) と EXISTS を効率的に実行するために、内部的にセミジョインを使います。SQL でセミジョインを直接書くことはできませんが、EXISTS と IN (subquery) はどちらも、オプティマイザにセミジョインの検討を促します。
トラブルシューティング
関連項目
- MySQL の JOIN のパフォーマンスとよくある間違い — この比較の JOIN 側を遅くするもの
- MySQL の CTE が派生テーブルより優れる場合 — 深く入れ子になったサブクエリに代わる、よりすっきりした選択肢
- MySQL のサブクエリと相関サブクエリ — それらの書き方を学ぶチュートリアルのレッスン
- MySQL の EXISTS — 行を決して増やさない形