-
IN
に対するサブクエリーの最適化は、=
演算子またはIN(
演算子の場合のようには効果的ではありません。value_list
)IN
サブクエリーのパフォーマンスが不十分である一般的な例では、サブクエリーが少数の行を返すのに対し、外部クエリーがサブクエリーの結果と比較して多数の行を返す場合があります。この問題は、
IN
サブクエリーを使用するステートメントでは、オプティマイザが相関サブクエリーに書き換えることにあります。非相関サブクエリーを使用する次のステートメントについて説明します。SELECT ... FROM t1 WHERE t1.a IN (SELECT b FROM t2);
オプティマイザがステートメントを相関サブクエリーに書き換えます。
SELECT ... FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE t2.b = t1.a);
内部クエリーと外部クエリーがそれぞれ
M
行とN
行を返す場合、実行時間は、非相関サブクエリーの場合の O(M
+N
) ではなく、O(M
×N
) になります。つまり、
IN
サブクエリーは、このサブクエリーが返す値と同じ値をリストするIN(
演算子を使用して作成されたクエリーよりもかなり遅くなる場合があります。value_list
) -
一般に、テーブルを変更することも、サブクエリーの同じテーブルから選択することもできません。たとえば、この制限は次の形式のステートメントに適用されます。
DELETE FROM t WHERE ... (SELECT ... FROM t ...); UPDATE t ... WHERE col = (SELECT ... FROM t ...); {INSERT|REPLACE} INTO t (SELECT ... FROM t ...);
例外:
FROM
句内の変更されたテーブルでサブクエリーを使用している場合、前述の禁止事項は適用されません。例:UPDATE t ... WHERE col = (SELECT * FROM (SELECT ... FROM t...) AS _t ...);
ここでは、
FROM
句内のサブクエリーの結果が一時テーブルとして格納されるので、t
内の対応する行は、t
に対する更新が行われる前にすでに選択されています。 -
行比較演算は一部のみサポートされています。
の場合、expr
[NOT] INsubquery
expr
はn
タプル (行コンストラクタ構文を使用して指定します) にでき、サブクエリーはn
タプルの行を返すことができます。したがって、許可されている構文は、具体的には
と表されますrow_constructor
[NOT] INtable_subquery
の場合、expr
op
{ALL|ANY|SOME}subquery
expr
はスカラー値にする必要があり、サブクエリーはカラムサブクエリーにする必要があります。複合カラム行を返すことはできません。
つまり、
n
タプルの行を返すサブクエリーの場合、次のものはサポートされています。(expr_1, ..., expr_n) [NOT] IN table_subquery
ただし、次のものはサポートされていません。
(expr_1, ..., expr_n) op {ALL|ANY|SOME} subquery
IN
の行比較がサポートされているのに、他はサポートされていない理由は、IN
が、=
比較およびAND
演算のシーケンスに、これを書き換えることによって実装されているためです。この方法は、ALL
、ANY
、SOME
には使用できません。 FROM
句のサブクエリーは相関サブクエリーにはできません。これらは、クエリー実行中にすべて実体化 (結果セットを生成するように評価) されるので、外部クエリーの行ごとに評価できません。MySQL 5.6.3 より前では、実体化は外部クエリーの評価の前に行われます。5.6.3 以降では、オプティマイザは、結果が必要になるまで実体化を遅らせ、これにより実体化を回避できます。セクション8.2.1.18.3「FROM 句内のサブクエリー (派生テーブル) の最適化」を参照してください。-
MySQL は特定のサブクエリー演算子にサブクエリーの
LIMIT
をサポートしていません。mysql> SELECT * FROM t1 -> WHERE s1 IN (SELECT s2 FROM t2 ORDER BY s1 LIMIT 1); ERROR 1235 (42000): This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
-
オプティマイザは、サブクエリーに対してよりも結合に対するほうが完成度が高いので、多くの場合、サブクエリーを使用するステートメントは、結合として書き換えた場合のほうが効率的に実行できます。
IN
サブクエリーをSELECT DISTINCT
結合として書き換えることができる場合には、例外が生じます。例:SELECT col FROM t1 WHERE id_col IN (SELECT id_col2 FROM t2 WHERE condition);
このステートメントは次のように書き換えることができます。
SELECT DISTINCT col FROM t1, t2 WHERE t1.id_col = t2.id_col AND condition;
-
MySQL では、サブクエリーで行をテーブルに挿入するなどのデータ変更の副作用があるストアドファンクションを参照できます。たとえば、
f()
が行を挿入する場合、次のクエリーはデータを変更できます。SELECT ... WHERE x IN (SELECT f() ...);
この動作は、SQL 標準に対する拡張です。MySQL では、オプティマイザでの処理方法の選択に応じて、特定のクエリーの実行ごとに
f()
が異なる回数実行される場合があるため、これにより不確定な結果が生成される可能性があります。ステートメントベースまたは混合形式のレプリケーションの場合、この不確定性には、このようなクエリーが、マスターとそのスレーブで異なる結果を生成することがあります。
MySQL 5.6.3 より前では、
FROM
句のサブクエリーは、結果を一時テーブルに実体化することにより評価され、このテーブルではインデックスが使用されません。5.6.3 以降では、クエリー実行がさらに高速になる場合は、オプティマイザは実体化されたテーブルにインデックスを作成します。セクション8.2.1.18.3「FROM 句内のサブクエリー (派生テーブル) の最適化」を参照してください。