サブクエリ構文

1. スカラ演算子としてのサブクエリ
2. サブクエリを利用した比較
3. ANYIN、そして SOME を持つサブクエリ
4. ALL を持つサブクエリ
5. 行サブクエリ
6. EXISTSNOT EXISTS
7. 相関サブクエリ
8. FROM 条項内のサブクエリ
9. サブクエリ エラー
10. 最適化サブクエリ
11. MySQL 初期バージョンにおいて、サブクエリの接合としての書き換え

サブクエリは、別のステートメント内の SELECT ステートメントです。

MySQL 4.1 から、MySQL 特有のいくつかの特徴と同様に、SQL スタンダードが要求する全てのサブクエリ型と演算子がサポートされています。

ここに、同じようなサブクエリの例があります。

SELECT * FROM t1 WHERE column1 = (SELECT column1 FROM t2);

この例の中では、SELECT * FROM t1 ...外部クエリ (または 外部ステートメント)であり、(SELECT column1 FROM t2)サブクエリ です。これは、サブクエリが外部クエリ内で ネスト化された という事であり、実際、サブクエリを別のサブクエリ内で、相当な深さまでネスト化する事が可能です。サブクエリは必ずカッコ内に表示されなければいけません。

サブクエリの主な利点は次のような物になります。

ここに、SQL スタンダードによって指定され、MySQL 内でサポートされているサブクエリ構文に関する主なポイントを説明するステートメントの例があります。

DELETE FROM t1
WHERE s11 > ANY
(SELECT COUNT(*) /* no hint */ FROM t2
WHERE NOT EXISTS
(SELECT * FROM t3
WHERE ROW(5*t2.s1,77)=
(SELECT 50,11*s1 FROM t4 UNION SELECT 50,77 FROM
(SELECT * FROM t5) AS t5)));

サブクエリは、スカラ(単一値)、単一行、単一カラム、またはテーブル(1つ、または複数カラムの、1つ、または複数行) を返す事ができます。これらはスカラ、カラム、行、そしてテーブル サブクエリと呼ばれます。次のセクションで説明されているように、頻繁に特定の種類の結果を返すサブクエリは、特定のコンテキストの中だけで利用する事ができます。

サブクエリを利用する事ができるステートメントのタイプには、いくつかの制限があります。サブクエリは、普通の SELECT が含む事のできるキーワードや条項を全て含む事ができます。それは DISTINCTGROUP BYORDER BYLIMIT、接合、インデックスヒント、UNION 構成、コメント、関数などです。

制限の1つは、サブクエリの外部ステートメントが SELECTINSERTUPDATEDELETESET、または DO のうちのどれか1つでなければいけないという事です。その他の制限は、現在はサブクエリの中でテーブルを変更したり、同じテーブルから選択する事ができないという事です。これは、DELETEINSERTREPLACEUPDATE、そして(サブクエリは SET 条項内で利用できる為) LOAD DATA INFILE のようなステートメントに適応します。

サブクエリ構文の特定型に関する性能問題を含む、サブクエリ利用に関する制限のさらなる総合的な説明に関しては、 項D.3. 「サブクエリの規制」 で紹介されています。

1. スカラ演算子としてのサブクエリ

簡単に言うと、サブクエリは単一値を戻すスカラ サブクエリという事になります。スカラ サブクエリは単純な演算子で、単一カラム値や直定数が正当であればほとんどどこでも利用する事ができ、データ タイプ、長さ、 NULL になり得るかどうかという指示など、全ての演算子が持つ特徴も全て持っています。ここにその例があります。

CREATE TABLE t1 (s1 INT, s2 CHAR(5) NOT NULL);
INSERT INTO t1 VALUES(100, 'abcde');
SELECT (SELECT s2 FROM t1);

この SELECT 内のサブクエリは、CHAR のデータ タイプを持ち、長さが5で、CREATE TABLE 時にデフォルトと同等の文字セットと照合が実施されており、そしてカラム内の値が NULL になり得るという指示を持つ単一値('abcde')を返します。実際は、ほとんどのサブクエリが NULL になり得ます。もし例で使用されたテーブルが空であれば、サブクエリの値は NULL になるでしょう。

スカラ サブクエリを利用できないコンテキストがいくつかあります。もしステートメントが直定数値だけを許容するなら、サブクエリを利用する事はできません。例えば、LIMIT が直定整数引数を要求し、LOAD DATA INFILE が直定数文字列ファイル名を要求します。これらの値を供給するのにサブクエリを利用する事はできません。

次のセクションにある、より質素な構造を含む (SELECT column1 FROM t1) の例を見る時、自分自身のコードが、それよりもさらに多様で、複雑な構造を含んでいると想像してください。

2つテーブルを作成すると仮定します。

CREATE TABLE t1 (s1 INT);
INSERT INTO t1 VALUES (1);
CREATE TABLE t2 (s1 INT);
INSERT INTO t2 VALUES (2);

次に、SELECT を実行します。

SELECT (SELECT s1 FROM t2) FROM t1;

2 の値を持つカラム s1 を含む行が t2 にあるので、その結果は 2 となります。

スカラ サブクエリは式の一部になり得ますが、もしそのサブクエリが関数に引数を与える演算子だとしても、括弧を忘れないでください。 例:

SELECT UPPER((SELECT s1 FROM t1)) FROM t2;

2. サブクエリを利用した比較

サブクエリの最もよく利用される方法はこの形の中にあります。

non_subquery_operand comparison_operator (subquery)

comparison_operator がこれらの演算子の1つであるところではこうです。

=  >  <  >=  <=  <>

例:

  ... 'a' = (SELECT column1 FROM t1)

かつては、サブクエリが正当であるたった1つの場所は比較の右側でしたし、いまだにいくつかの古い DBMS がこれを主張しています。

ここに、接合と共に実行できない共通形サブクエリ比較の例があります。これは、テーブル t2 の最大値と同等の、テーブル t1 内の全ての値を検出します。

SELECT column1 FROM t1
WHERE column1 = (SELECT MAX(column2) FROM t2);

ここに、テーブルの1つに対する凝集を含む為、接合と共に利用する事ができないもうひとつの例があります。これは、与えられたカラムの中で2回現れる値を含む、テーブル t1 の中の全ての行を検出します。

SELECT * FROM t1 AS t
WHERE 2 = (SELECT COUNT(*) FROM t1 WHERE t1.id = t.id);

これらの演算子の1つを利用して実行された比較には、= は行サブクエリと共に利用できるという例外がありますが、サブクエリはスカラを返さなければいけません。詳しくは 項5. 「行サブクエリ」 を参照してください。

3. ANYIN、そして SOME を持つサブクエリ

構文:

operand comparison_operator ANY (subquery)
operand IN (subquery)
operand comparison_operator SOME (subquery)

比較演算子の後に続かなければいけない ANY キーワードは、「もしサブクエリが返すカラム内の値の ANY に対する比較が TRUE であれば、TRUE を返す」 という事を意味します。例:

SELECT s1 FROM t1 WHERE s1 > ANY (SELECT s1 FROM t2);

テーブル (10) を含むテーブル t1 内に行があると仮定してください。10 以下である値 7t2 の中にあるので、もしテーブル t2(21,14,7) を含むなら、その式は TRUE です。もしテーブル t2(20,10) を含むか、テーブル t2 が空であれば、その式は FALSE です。もしテーブル t2(NULL,NULL,NULL) を含むなら、その式は UNKNOWN です。

サブクエリと共に利用される時、IN という言葉は = ANY のエイリアスとなります。従って、これら2つのステートメントは同じになります。

SELECT s1 FROM t1 WHERE s1 = ANY (SELECT s1 FROM t2);
SELECT s1 FROM t1 WHERE s1 IN    (SELECT s1 FROM t2);

式のリストと共に利用される時、IN= ANY は同義語ではありません。IN は式のリストを取る事ができますが、= ANY はできません。詳しくは 項 「比較関数と演算子」 を参照してください。

NOT IN<> ANY のエイリアスではありませんが、<> ALL のエイリアスです。詳しくは 項4. 「ALL を持つサブクエリ」 を参照してください。

SOME という言葉は ANY のエイリアスです。従って、これら2つのステートメントは同じになります。

SELECT s1 FROM t1 WHERE s1 <> ANY  (SELECT s1 FROM t2);
SELECT s1 FROM t1 WHERE s1 <> SOME (SELECT s1 FROM t2);

SOME が利用される事はほとんどありませんが、なぜこれが役に立つのか、この例が表しています。通常、英語で 「a はどの b とも同等ではない」 と言うと、「 a と等しい b は無い」 と解釈されますが、SQL 構文ではそのような意味ではないのです。その構文は、「a と同等ではない b がいくつかある」 と意味します。<> SOME を代わりに利用すると、そのクエリが本当に意味している事を全員がきちんと理解する助けになります。

4. ALL を持つサブクエリ

構文:

operand comparison_operator ALL (subquery)

比較演算子の後に続かなければいけない ALL という言葉は、「もしサブクエリが返すカラム内の値の ALL に対する比較が TRUE であれば、TRUE を返す」 という事を意味します。例:

SELECT s1 FROM t1 WHERE s1 > ALL (SELECT s1 FROM t2);

テーブル (10) を含むテーブル t1 内に行があると仮定してください。10t2 の中の3つの値全てよりも大きいので、もし t2(-5,0,+5) を含むなら、その式は TRUE です。10 よりも大きい単一値 12 がテーブル t2 にあるので、もしテーブル t2(12,6,NULL,-100) を含むなら、その式は FALSE です。もしテーブル t2(0,NULL,1) を含むなら、その式は 不明 (NULL) です。

最後に、もしテーブル t2 が空なら、その結果は TRUE です。従って、テーブル t2 の時、次のステートメントは TRUE です。

SELECT * FROM t1 WHERE 1 > ALL (SELECT s1 FROM t2);

しかし、テーブル t2 が空の時、このステートメントは NULL です。

SELECT * FROM t1 WHERE 1 > (SELECT s1 FROM t2);

そして、テーブル t2 が空の時、次のステートメントは NULL です。

SELECT * FROM t1 WHERE 1 > ALL (SELECT MAX(s1) FROM t2);

通常、NULL 値を含むテーブル空のテーブル は 「 エッジ ケースです。」サブクエリ コードを書き込む時、必ずそれら2つの可能性を考慮したかどうか、確認してください。

NOT IN<> ALL のエイリアスです。従って、これら2つのステートメントは同じになります。

SELECT s1 FROM t1 WHERE s1 <> ALL (SELECT s1 FROM t2);
SELECT s1 FROM t1 WHERE s1 NOT IN (SELECT s1 FROM t2);

5. 行サブクエリ

この時点までの話は、単一値や値のカラムを返すサブクエリなどのような、スカラやカラム サブクエリに関しての物でした。行サブクエリ は単列を戻し、ひいては複数のカラム値を返す事ができるサブクエリ異型です。ここに2つ例があります。

SELECT * FROM t1 WHERE (1,2) = (SELECT column1, column2 FROM t2);
SELECT * FROM t1 WHERE ROW(1,2) = (SELECT column1, column2 FROM t2);

もしテーブル t2column1 = 1column2 = 2 の場所に行を持っていれば、ここにあるクエリは両方 TRUE です。

(1,2)ROW(1,2) は時々 row constructors と呼ばれます。それら2つは同等の物です。それらは、別のコンテキストの中でも正当です。例えば、次の2つのステートメントは意味的に同等です。(1つ目は MySQL 5.1.12 まで最適化する事ができませんが)

  SELECT * FROM t1 WHERE (column1,column2) = (1,1);
  SELECT * FROM t1 WHERE column1 = 1 AND column2 = 1;

行コンストラクタは通常、2つ以上のカラムを返すサブクエリを持つ比較に対して利用します。例えば、次のクエリは要求に答え、「テーブル t2 にも存在する テーブル t1 内の全ての行を検出します。

SELECT column1,column2,column3
FROM t1
WHERE (column1,column2,column3) IN
(SELECT column1,column2,column3 FROM t2);

6. EXISTSNOT EXISTS

もしサブクエリが行を返せば、EXISTS subqueryTRUE で、NOT EXISTS subqueryFALSE です。例:

SELECT column1 FROM t1 WHERE EXISTS (SELECT * FROM t2);

もともと、EXISTS サブクエリは SELECT * で開始しますが、SELECT 5SELECT column1、またそれ以外のどんな物でも開始する事ができます。MySQL はそのようなサブクエリの中では SELECT リストを無視するので、何も変わらないのです。

先ほどの例では、もし t2NULL 値しか含まない物でも良いので、何かの行を含むのなら、EXISTS の条件は TRUE となります。[NOT] EXISTS サブクエリは通常相互関係を持つので、実際はこれはよくあるような例ではありません。ここに、もう少し現実的な例があります。

  • 複数の町には、どんな種類のお店がありますか?

    SELECT DISTINCT store_type FROM stores
      WHERE EXISTS (SELECT * FROM cities_stores
                    WHERE cities_stores.store_type = stores.store_type);
  • 町ではないところには、どんな種類のお店がありますか?

    SELECT DISTINCT store_type FROM stores
      WHERE NOT EXISTS (SELECT * FROM cities_stores
                        WHERE cities_stores.store_type = stores.store_type);
  • 全ての町には、どんな種類のお店がありますか?

    SELECT DISTINCT store_type FROM stores s1
      WHERE NOT EXISTS (
        SELECT * FROM cities WHERE NOT EXISTS (
          SELECT * FROM cities_stores
           WHERE cities_stores.city = cities.city
           AND cities_stores.store_type = stores.store_type));

最後の例は、二重にネスト化された NOT EXISTS クエリです。それは、NOT EXISTS 条項の中に NOT EXISTS 条項を持っている、という事です。これは、正式に次の質問 「Stores にはないお店がある町は存在しますか」? という質問に答えます。しかし、ネスト化した NOT EXISTS が、次の質問 「x は全ての yTRUE ですか?」 に答える、と言う方が簡単です。

7. 相関サブクエリ

相関サブクエリ は、外部クエリ内にも現れるテーブルの参照を含むサブクエリです。例:

SELECT * FROM t1 WHERE column1 = ANY
(SELECT column1 FROM t2 WHERE t2.column2 = t1.column2);

サブクエリの FROM 条項がテーブル t1 に言及しなくても、サブクエリは t1 のカラムへの参照を含むという事を覚えておいて下さい。ですので、MySQL はサブクエリの外側を見て、外部クエリ内の t1 を見付けます。

テーブル t1column1 = 5column2 = 6 の場所で行を含み、一方、 テーブル t2column1 = 5column2 = 7 の場所で行を含むと仮定してください。単純な式 ... WHERE column1 = ANY (SELECT column1 FROM t2)TRUE となるでしょうが、例の中では、サブクエリ内の WHERE 条項は FALSE ですので、((5,6)(5,7) と同等ではない為) このサブクエリ全体としては FALSE です。

スコープ ルール: MySQL は内側から外側まで評価します。例:

SELECT column1 FROM t1 AS x
WHERE x.column1 = (SELECT column1 FROM t2 AS x
WHERE x.column1 = (SELECT column1 FROM t3
WHERE x.column2 = t3.column1));

このステートメントの中では、SELECT column1 FROM t2 AS x ...t2 をリネームするので、x.column2 はテーブル t2 内のカラムでなければいけません。SELECT column1 FROM t1 ...とても遠くにある 外部クエリなので、これはテーブル t1 内のカラムではありません。

HAVINGORDER BY 条項内のサブクエリに対しては、MySQL は外部選択リストからもカラム名を探します。

特定の場合には、相関サブクエリは最適化されます。例:

val IN (SELECT key_val FROM tbl_name WHERE correlated_condition)

そうでなければ、それらは役に立たず、スピードも遅くなりがちです。クエリを接合として書き換える事で、性能を向上させる事ができるかもしれません。

相関サブクエリは、外部クエリからの総計関数の結果を参照する事ができません。

8. FROM 条項内のサブクエリ

サブクエリは SELECT ステートメントの FROM 条項内で正当です。実際の構文はこれです。

SELECT ... FROM (subquery) [AS] name ...

[AS] name 条項は強制なので、FROM 条項内の全てのテーブルは名前を持つ必要があります。subquery 選択リスト内の全てのカラムは固有の名前を持たなければいけません。このマニュアルの中で 「派生テーブル」 という言葉が利用されている他の場所で、この構文の説明を見付ける事ができます。

説明する為に、このテーブルを持っていると仮定してください。

CREATE TABLE t1 (s1 INT, s2 CHAR(5), s3 FLOAT);

ここに、この例のテーブルを利用して、FROM 条項の中でサブクエリを利用する方法の説明があります。

INSERT INTO t1 VALUES (1,'1',1.0);
INSERT INTO t1 VALUES (2,'2',2.0);
SELECT sb1,sb2,sb3
FROM (SELECT s1 AS sb1, s2 AS sb2, s3*2 AS sb3 FROM t1) AS sb
WHERE sb1 > 1;

結果: 2, '2', 4.0.

ここに別の例があります。グループ分けされたテーブルの、合計セットの平均を知りたいと仮定します。これは機能しません。

SELECT AVG(SUM(column1)) FROM t1 GROUP BY column1;

しかし、このクエリは要求された情報を提供します。

SELECT AVG(sum_column1)
FROM (SELECT SUM(column1) AS sum_column1
FROM t1 GROUP BY column1) AS t1;

サブクエリの中で利用されたカラム名(sum_column1) が外部クエリの中で認められている事に注意してください。

FROM 条項内のサブクエリは、スカラ、カラム、行、そしてテーブルを返す事ができます。FROM 条項内のサブクエリは、JOIN 演算の ON 条項内で利用されない限り、相関サブクエリになる事ができません。

FROM 条項内のサブクエリは、EXPLAIN ステートメント(派生テンポラリ テーブルが作られた) に対しても実行する事ができます。これは、上位レベルクエリが最適化の段階で全てのテーブルの情報を必要とする為に起こります。

9. サブクエリ エラー

これらはサブクエリにだけ適応するエラーです。このセクションでは、それらについて説明していきます。

  • サポートされていないサブクエリ構文

    ERROR 1235 (ER_NOT_SUPPORTED_YET)
    SQLSTATE = 42000
    Message = "This version of MySQL does not yet support
    'LIMIT & IN/ALL/ANY/SOME subquery'"

    これは、次の形のステートメントはまだ機能しないと言う意味です。

    SELECT * FROM t1 WHERE s1 IN (SELECT s2 FROM t2 ORDER BY s1 LIMIT 1)
  • サブクエリからの不正カラム数

    ERROR 1241 (ER_OPERAND_COL)
    SQLSTATE = 21000
    Message = "Operand should contain 1 column(s)"

    このエラーは、このような場合に起こります。

    SELECT (SELECT column1, column2 FROM t2) FROM t1;

    もし比較する事が目的であれば、複合カラムを帰すサブクエリを利用すると良いでしょう。詳しくは 項5. 「行サブクエリ」 を参照してください。しかし、別のコンテキスト内では、サブクエリはスカラ演算子でなければいけません。

  • サブクエリからの不正行数

    ERROR 1242 (ER_SUBSELECT_NO_1_ROW)
    SQLSTATE = 21000
    Message = "Subquery returns more than 1 row"

    このエラーは、サブクエリが複数の行を返すステートメントで起こります。次の例を考えてみて下さい。

    SELECT * FROM t1 WHERE column1 = (SELECT column1 FROM t2);

    もし SELECT column1 FROM t2 が行を1つだけ返せば、その前のクエリは機能します。もしサブクエリが複数の行を返せば、エラー 1242が起きます。その場合、クエリは次のように書き直されなければいけません。

    SELECT * FROM t1 WHERE column1 = ANY (SELECT column1 FROM t2);
  • サブクエリ内の不正使用されたテーブル

    Error 1093 (ER_UPDATE_TABLE_USED)
    SQLSTATE = HY000
    Message = "You can't specify target table 'x'
    for update in FROM clause"

    このエラーは次のような場合に起きます。

    UPDATE t1 SET column2 = (SELECT MAX(column1) FROM t1);

    サブクエリは、SELECT ステートメント同様 UPDATEDELETE ステートメント内で正当なので、UPDATE ステートメント内で、割り当ての為にサブクエリを利用する事ができます。しかし、同じテーブルを(この場合、テーブル t1)サブクエリの FROM 条項と更新ターゲットの両方に対して利用する事はできません。

トランザクション ストレージ エンジンに対しては、サブクエリの失敗は、ステートメント全体の失敗を引き起こします。非トランザクション ストレージ エンジンに対しては、エラーが起こる前に行われたデータ修正が保持されます。

10. 最適化サブクエリ

開発が進行中なので、最適化についての情報は長期的に信頼性があります。次のリストは、利用してみたいいくつかの興味深いトリックを紹介しています。

  • サブクエリ内の行数や行の順番に影響を与えるサブクエリ条項を利用してください。例:

    SELECT * FROM t1 WHERE t1.column1 IN
    (SELECT column1 FROM t2 ORDER BY column1);
    SELECT * FROM t1 WHERE t1.column1 IN
    (SELECT DISTINCT column1 FROM t2);
    SELECT * FROM t1 WHERE EXISTS
    (SELECT * FROM t2 LIMIT 1);
  • サブクエリと接合を置き換えてください。例えば、次の例を

    SELECT DISTINCT column1 FROM t1 WHERE t1.column1 IN (
    SELECT column1 FROM t2);

    この次の物と置き換えます。

    SELECT DISTINCT t1.column1 FROM t1, t2
    WHERE t1.column1 = t2.column1;
  • サブクエリをサポートしない古いバージョンの MySQL の互換性で、いくつかのサブクエリは接合に変形する事ができます。しかし、いくつかの場合で、サブクエリを接合に変換する事で性能を向上させる事ができます。詳しくは 項11. 「MySQL 初期バージョンにおいて、サブクエリの接合としての書き換え」 を参照してください。

  • 条項をサブクエリの外から中に移動させて下さい。例えば、次の例を

    SELECT * FROM t1
    WHERE s1 IN (SELECT s1 FROM t1 UNION ALL SELECT s1 FROM t2);

    この次のクエリの代わりに利用します。

    SELECT * FROM t1
    WHERE s1 IN (SELECT s1 FROM t1) OR s1 IN (SELECT s1 FROM t2);

    別の例として、このクエリを、

    SELECT (SELECT column1 + 5 FROM t1) FROM t2;

    この次のクエリの代わりに利用します。

    SELECT (SELECT column1 FROM t1) + 5 FROM t2;
  • 相関サブクエリの代わりに行サブクエリを利用してください。例えば、次の例を

    SELECT * FROM t1
    WHERE (column1,column2) IN (SELECT column1,column2 FROM t2);

    この次のクエリの代わりに利用します。

    SELECT * FROM t1
    WHERE EXISTS (SELECT * FROM t2 WHERE t2.column1=t1.column1
    AND t2.column2=t1.column2);
  • a <> ALL (...) ではなく NOT (a = ANY (...)) を利用してください。

  • x=1 OR x=2 ではなく x = ANY (table containing (1,2)) を利用してください。

  • EXISTS ではなく = ANY を利用してください。

  • 必ず1つの行を返す相関サブクエリに対しては、IN は必ず = よりも遅いです。例えば、次の例を

    SELECT * FROM t1 WHERE t1.col_name
    = (SELECT a FROM t2 WHERE b = some_const);

    この次のクエリの代わりに利用します。

    SELECT * FROM t1 WHERE t1.col_name
    IN (SELECT a FROM t2 WHERE b = some_const);

これらのトリックは、プログラムを早くしたり、遅くしたりする可能性があります。BENCHMARK() 関数のような MySQL 機能を利用すると、今の状況を改善する為のアイデアを見付ける事ができます。詳しくは 項 「情報関数」 を参照してください。

MySQL 自体が行う最適化のいくつかは次のような物です。

  • MySQL は非相関サブクエリを一度だけ実行します。EXPLAIN を利用して、与えられたサブクエリが本当に非相関であるかどうかを確認してください。

  • MySQL は、サブクエリ中の選択リスト カラムがインデックスされる可能性の利点を利用する為に INALLANY、そして SOME サブクエリを書き換えます。

  • MySQL は、EXPLAIN が特別な接合タイプ(unique_subquery または index_subquery)として表現する、インデックス検索機能を利用した次の形のサブクエリを置き換えます。

    ... IN (SELECT indexed_column FROM single_table ...)
  • MySQL は、NULL 値か空のセットが関連していない限り、MIN() または MAX() を含む式を利用した次の形の式を強化します。

    value {ALL|ANY|SOME} {> | < | >= | <=} (non-correlated subquery)

    例えば、次の WHERE 条項ですが、

    WHERE 5 > ALL (SELECT x FROM t)

    オプチマイザによってこのように扱われるでしょう。

    WHERE 5 > (SELECT MAX(x) FROM t)

MySQL がどのようにサブクエリを変形させるか」 というタイトルの章が、MySQL 内部マニュアルの http://dev.mysql.com/doc/ で参照可能です。

11. MySQL 初期バージョンにおいて、サブクエリの接合としての書き換え

MySQL の初期バージョンでは(MySQL 4.1以前)、INSERT ... SELECT ...REPLACE ... SELECT ... の形のネスト化されたクエリだけがサポートされていました。MySQL 5.1 ではそうでないとしても、値のセットの中でメンバーシップをテストする別の方法があるという事も事実です。また、場合によっては、クエリをサブクエリ無しで再書き込みする事だけでなく、先ほどの別のテクニックを利用した方がより効果的であるというのも事実です。その1つが IN() コンストラクトです。

例えば、次の例は

SELECT * FROM t1 WHERE id IN (SELECT id FROM t2);

次のように書き換えられます。

SELECT DISTINCT t1.* FROM t1, t2 WHERE t1.id=t2.id;

次のようなクエリは、

SELECT * FROM t1 WHERE id NOT IN (SELECT id FROM t2);
SELECT * FROM t1 WHERE NOT EXISTS (SELECT id FROM t2 WHERE t1.id=t2.id);

IN() を利用して次のように書き換えられます。

SELECT table1.* FROM table1 LEFT JOIN table2 ON table1.id=table2.id
WHERE table2.id IS NULL;

サーバの方が上手最適化する事ができるかもしれないので、— MySQL サーバだけに特有であるというわけではない、LEFT [OUTER] JOIN はそれに同等のサブクエリよりも早いかもしれません。 SQL-92 以前は外部接合が存在しなかったので、特定の作業をする為には、サブクエリが唯一の方法でした。現在は、MySQL サーバやその他のデータベース システムが様々なタイプの外部接合を提供しています。

MySQL サーバは、1つのテーブル、または複数のテーブルから一度に出される情報に基づき、行を効果的に削除する為に利用する事ができる複合テーブル DELETE ステートメントをサポートします。複合テーブル UPDATE ステートメントもまたサポートされています。