サブクエリについて
サブクエリは副問合せ
とも呼ばれ、SELECT や INSERT、UPDATE、DELETE クエリの中で SELECT といったクエリを実行するものです。
以下の例では、 SELECT サブクエリを検索条件にしています。サブクエリを使用することで保存されたデータをメインクエリの検索条件とすることができます。
サブクエリを先に実行するために必ずカッコ(
と)
で囲むようにします。
上記の例での実行は以下のように進みます。
まずサブクエリが実行されます。
上記の結果から以下のようなクエリが実行されます。
サブクエリによって作成されたテーブルデータはメインクエリの実行の終了とともにクリアされます。
メインクエリが INSERT や UPDATE、DELETE といったクエリである場合もあります。
サブクエリは取得したいデータに平均値や最大値を含めたいときにも使用することができます。
上記の例のように同一のテーブルである場合には AVG(amount)
のみでも問題はありません。
検索条件として平均以上の行を抽出する場合にも使用できます。
このようにサブクエリとはメインクエリの実行の前にクエリを実行します。
サブクエリが返す値
サブクエリの返り値には以下のような場合があることが考えられます。
- 列が1つで行が1つ
- 列が1つで複数の行
- 複数の列の複数の行
サブクエリの結果を比較演算子(=、<>、<=、>=、<、>)のみで比較できるのは列が1つで行が1つ
を返す場合のみです。
サブクエリが列が1つで複数の行
や複数の列の複数の行
を返す場合に比較演算子のみではエラーになります。
比較演算子のみではエラーになりますが、IN 演算子や ANY 演算子、 ALL 演算子を使用することでサブクエリが複数の値を返す場合でもエラーなく結果を得ることができます。
詳しくは以下のサブクエリの利用方法(非相関サブクエリ)
で解説しています。
非相関サブクエリと相関サブクエリ
サブクエリの利用方法には、サブクエリとメインクエリの関係によって以下のような場合があります。
- サブクエリはメインクエリと同じテーブルのカラムを参照しない(非相関サブクエリ)
- サブクエリがメインクエリと同じテーブルのカラムが参照されている(相関サブクエリ)
非相関サブクエリも相関サブクエリもサブクエリの利用方法なのですが、サブクエリとメインクエリが同じテーブルのカラムを参照しているという点があるか/ないかの違いだけです。
例えば、相関サブクエリは以下のようにサブクエリ内でメインクエリと同じテーブルのカラムを参照している場合です。
非相関サブクエリはサブクエリ内でメインクエリと同じテーブルのカラムを参照している
という点を省けばサブクエリの利用方法として解説できてしまいます。
非相関サブクエリは以下のサブクエリの利用方法(非相関サブクエリ)
を参照してください。相関サブクエリについては以下の相関サブクエリ
を参照してください。
サブクエリと比較演算(非相関サブクエリ)
以下はサブクエリとしての解説の続きとなります。
非相関サブクエリはサブクエリ内でメインクエリと同じテーブルのカラムを参照している
という点を省けばサブクエリの利用方法として解説できますので重複を避けるためにここではサブクエリの利用方法
としています。
サブクエリにおける注意点
サブクエリの結果を比較演算子(=、<>、<=、>=、<、>)のみで比較できるのは、サブクエリが列が1つで行が1つ
を返す場合のみです。
サブクエリが列が1つで複数の行
や複数の列の複数の行
を返す場合に比較演算子のみではエラーになります。
エラーが帰らないようにするには IN 演算子を使用するか ANY、ALL 演算子を使用します。以下はこの点について解説します。
サブクエリが1つしかデータを返さない場合は以下の場合でもエラーになりません。
複数の行を返すようなサブクエリはSubquery returns more than 1 row
といった1つ以上の行は比較できないというエラーになります。
このようにサブクエリが列が1つで複数の行を返す
ような場合でもメインクエリがエラーなく実行する方法があります。
列が1つで複数行を返すサブクエリ
IN 演算子
上記の例のメインクエリの比較演算子を IN 演算子に変更するとエラーが起きません。
NOT IN 演算子はサブクエリの結果以外に該当するデータを抽出できます。
IN 演算子についてはWHERE 句ページの範囲条件、IN 演算子と NOT IN 演算子
を参照してください。
ANY 演算子
似た機能をもつ演算子に ANY 演算子 があります。
これらの演算子には比較演算子(=、<>、<=、>=、<、>)と合わせて使用します。比較演算子に ANY 演算子を付けることで結果が望み通りかを検証することができます。
ANY では以下のようになります。
IN 演算子での結果と同じで、想定された結果であると思います。
ALL 演算子
比較演算子(=、<>、<=、>=、<、>)と合わせて使用する演算子に ALL があります。
ALL 演算子を使用するとサブクエリが複数のデータを返してもエラーにはなりません。
しかし、結果は空であることが返ってきます。
ALL 演算子はサブクエリが返す値とメインクエリのカラム値(上記の例では goods_id )の比較において、比較できない( UNKOWN )があると結果が空になります。
上記の例はサブクエリが複数のデータを返した時にメインクエリのカラムと比較ができないために結果が空になったのです。
サブクエリとの比較対象が1つのときは想定通りの結果が返ってきます。
サブクエリが返す値が複数の場合には想定通りの結果が返ってきません。
ALL 演算子はサブクエリが複数の値を返すと比較ができないようです。例えば以下のようにサブクエリが返す値が1つのときは想定通りの結果になります。
このことは ALL 演算子がなくても同じことが言えます。ただ、 ALL 演算子を使うとエラーとして終了することはありません。
ALL 演算子は、API でサブクエリを利用するときにデータベースでのエラーで処理が終了するといったことを防ぐことができます。
列が複数で複数行を返すサブクエリ
列が複数で複数行を返すようなサブクエリを検索条件として比較するには IN 演算子を使用します。
IN 演算子には複数のカラムと値を比較する機能があります。
もしくは検索条件の中で個々に比較することも考えられます。
EXISTS 演算子
EXISTS 演算子とは、この演算子に続くサブクエリがデータを返すか/否かを判定する演算子です。
例えば以下は、商品マスターに記録された商品名の中から売上があったものだけを抽出しています。
このように、EXISTS 演算子によってマスターに関連付けられたテーブルにデータがあるか/無いかを確認することができます。
マスターテーブルとの関連付けは FOREIGN KEY(外部キー制約)によって作成します。
上記の例とは逆に NOT EXISTS 演算子を利用すれば売上がなかった商品を確認することができます。
MySQL や PostgreSQL で EXISTS 演算子はサブクエリのみが処理できるようです。
相関サブクエリには、UPDATE や DELETE を行う場合にサブクエリも同じテーブルを参照する場合もあります。
例えば、伝票を取引ごとに記録するためにメインの取引名や日付を記録したテーブルと詳細な取引内容をテーブルがあるとします。
メインは作成したけど詳細データは作成していない場合にメインのデータを削除したいとします。
そのような場合は、以下のように詳細データのない場合は該当するメインデータを抽出削除します。
上記は例だけでテストデータは作成していません。
注意点としては UPDATE や DELETE とサブクエリ組み合わせでは AS でのテーブルエリアスは機能しない場合があります。