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 T1
SELECT
句での副問合せは1つの列のみ指定する必要があります。
レスポンスに注意
SELECT
句での副問合せは1つの列しか指定できないため、副問合せの数が増えてしまいがちです。
同じような条件のSQL
はWITH
句でまとめるとパフォーマンスが改善することが多いです。
WITH
句の基本的な使い方についてはSQL WITH句で同じSQLを1つのSQLに共通化するで紹介しています。