SQL SELECT句での副問合せの基本的な考え方と注意点
SELECT句での副問合せをうまく利用すれば、集計単位の異なる値を同時に取得できます。
以下のようなテーブルがあるとします。
| id | first_name | last_name | department_id | height |
|---|---|---|---|---|
| 1 | 一郎 | 山田 | 1 | 170 |
| 2 | 次郎 | 佐藤 | 2 | 175 |
| 3 | 三郎 | 田中 | 1 | 185 |
| 4 | 四郎 | 鈴木 | 2 | 155 |
SQL GROUP BYで複数の行を集計して1行で取得するで紹介したように、 部署(department_id)ごと の平均身長、最大身長を取得したいときは以下のようなSQLになります。
SELECT
department_id,
AVG(height) AS avg_height,
MAX(height) AS max_height
FROM
employee
GROUP BY
department_id| department_id | avg_height | max_height |
|---|---|---|
| 1 | 177.5 | 185 |
| 2 | 165 | 175 |
特定の列のみ集計行にする
上記のSQLではGROUP BYで行を集約して取得するので、部署ごとの行となります。
ここで社員毎の行のまま各社員が所属する部署の平均身長、最大身長を取得したいときにSELECT句での副問合せを使用します。
SQL PARTITION BYの基本と効率的に集計する便利な方法で紹介したように、分析関数を使用すればもっとシンプルに取得できますが、説明の都合であえて以下の方法で取得します。
SELECT
T1.id,
T1.first_name,
T1.last_name,
T1.department_id,
(
SELECT
AVG(SUB1.height)
FROM
employee SUB1
WHERE
T1.department_id = SUB1.department_id
) AS avg_height,
(
SELECT
MAX(SUB1.height)
FROM
employee SUB1
WHERE
T1.department_id = SUB1.department_id
) AS max_height
FROM
employee T1| id | first_name | last_name | department_id | avg_height | max_height |
|---|---|---|---|---|---|
| 1 | 一郎 | 山田 | 1 | 177.5 | 185 |
| 2 | 次郎 | 佐藤 | 2 | 165 | 175 |
| 3 | 三郎 | 田中 | 1 | 177.5 | 185 |
| 4 | 四郎 | 鈴木 | 2 | 165 | 175 |
SQL FROM句での副問合わせの基本的な考え方と注意点で紹介したように、FROM句での副問合せはインラインビューと呼ぶのに対して、SELECT句やWHERE句での副問合せをサブクエリと呼びます。
注意点
メインのSQLの値が使用可能
上記SQLのように、サブクエリのWHERE句でメインのSQLの値(T1.department_id)を使用しています。
これにより、取得した結果に対してもう一度SQLを実行するようなイメージで値を取得できます。
上記SQLでは社員一覧を取得→所属する部署の平均身長、最大身長を取得という2つのSQLをサブクエリを使用して1回のSQLで取得しています。
取得結果が1行のみとなる必要がある
上記SQLでは集計関数を使用しているため取得結果が複数件になることはありませんが、取得結果は必ず1行になる必要があります。
SELECT
T1.COL1,
(
--サブクエリの取得結果は必ず1行となること
SELECT
SUB1.COL1
FROM
B SUB1
WHERE
T1.COL2 = SUB1.COL2
)
FROM
A T1取得結果が1行になるかどうかは実際にSQLを実行してみなければわかりません。
開発時のテストデータではたまたま1行で取得できていたためエラーにならなかったが、本番環境で実行するとエラーになってしまうということがないように、1件のみ返すことが保証できるSQLにする必要があります。
(0件の場合でもエラーにはなりません。0件の場合はNULLとして扱われます。)
1つの列のみ指定可能
平均身長と最大身長を同時に取得しようと以下のSQLを実行してもエラーになってしまいます。
SELECT
T1.id,
T1.first_name,
T1.last_name,
T1.department_id,
(
SELECT
AVG(SUB1.height),
MAX(SUB1.height)
FROM
employee SUB1
WHERE
T1.department_id = SUB1.department_id
)
FROM
employee T1SELECT句での副問合せは1つの列のみ指定する必要があります。
レスポンスに注意
SELECT句での副問合せは1つの列しか指定できないため、副問合せの数が増えてしまいがちです。
同じような条件のSQLはWITH句でまとめるとパフォーマンスが改善することが多いです。
WITH句の基本的な使い方についてはSQL WITH句で同じSQLを1つのSQLに共通化するで紹介しています。