注意

本記事は、SELECT文 (SQL) の応用的な内容について記載しています。
そのため、”SELECT文の基本的な文法を知っている”ことを前提とした内容になります。

初めに

データ抽出のSQLでは「副問合せ」(サブクエリ)という書き方があります。
SQLを学ぶ中で、比較的理解するのが難しいかもしれません。
特に「”相関”副問合せ」というのが頭を悩ませるかと…
そういう背景もあり、今回は、「副問合せ」について説明していきます。

「副問い合わせ」とは

簡単に言うと、「SELECT文 の中に SELECT文 を書く」という書き方です。
下記コードのような枠組みで書くことができ、要は、” 抽出テーブル や 条件値(or リスト) を select文で指定する “という書き方になります。
外枠の問合せ とは別に、内包的に問合せを書くので「”副”問合せ」という感じですね。(下記コードは、”抽出テーブルをselect文で指定”の形です。)

select 
from (
    select 
    from
);

「副問合せ」の例

下記データに対して、”営業部の社員名を抽出”するように上司から求められたとします。

[データ]

部署ID部署名
1人事
2総務
3営業
部署マスタ
社員ID社員名部署ID
1田中2
2佐藤2
3高橋3
4飯田1
5吉田3
社員マスタ

[求める抽出結果]

社員名
高橋
吉田
営業部の社員名

上記を「副問合せ」で行う場合、下記例のように書くことができます。

例1. from句で副問合せ

select t1.社員名
from 社員マスタ t1 
    inner join (
        select 部署ID
        from 部署マスタ
        where 部署名 = '営業'
    ) t2
    on t1.部署ID = t2.部署ID
;

例2. where句で副問合せ

select t1.社員名
from 社員マスタ t1
where t1.部署ID = (
    select 部署ID
    from 部署マスタ
    where 部署名 = '営業'
);

例2 (where句で副問合せ) では、注意点があります。
上記例2のwhere句では、等号条件(=)で指定していますが、副問合せ箇所の抽出結果が “絶対に1行” である場合のみ等号条件で指定しましょう。
(今回のデータでは、”部署で一意になる”部署マスタを抽出対象にしているので、抽出結果が”絶対に1行”という前提で例示しています。)
その抽出結果が、複数行になった時は、”値=リスト” の形式になるのでエラーが発生します
副問合せ箇所の抽出結果が複数行になる時もある” ならば、in句で指定しましょう。(値 in 副問合せ)

「相関副問合せ」とは

上記で説明した「副問合せ」の特殊な形です。
簡単に言うと、「副問合せ内(内枠)で、外枠で抽出するテーブル・項目を用いる。」という書き方です。
下記コードのような形で書きます。

select t1.x
from tabel_1 t1
where t1.y = (
    select t2.y
    from table_2 t2
    where t1.z = t2.z
);

パッと見たところでは、”何をもって相関副問合せなのか”よく分からないですよね…
着目して欲しいのは、「“where句にある副問合せ”のwhere句 (6行目) 」です。
条件式を見ると、table_1の項目(t1.z)を使っていますね。
そして、table_1は、副問合せの外枠で指定しています。(2行目のfrom句)
そこがポイントです。
(外枠で指定した) table_1のデータによって、副問合せの内容 (条件式) が動的に変化する形になってます。
つまり、「副問合せの内容が、(外枠での指定データに合わせて) 動的に変化する」ような書き方を「相関副問合せ」と言います。

「相関副問合せ」の例

上記[3. 「副問合せ」の例]のデータを抽出対象にします。
但し、今回は部署マスタに1列「部長ID」を追加します。
さて、今回は、”営業部の部長の社員名”の抽出を求められたとします。

[データ]

部署ID部署名部長ID
1人事4
2総務2
3営業5
部署マスタ
社員ID社員名部署ID
1田中2
2佐藤2
3高橋3
4飯田1
5吉田3
社員マスタ

[求める抽出結果]

社員名
吉田
営業部部長の社員名

上記を「相関副問合せ」で行う場合、下記例のように書くことができます。
exists句を用いる方が多少簡潔に書けますが、exists句は使用機会も限られるので、よく知らない方が比較的多いと思います。
その場合は例1を、existsを知っている方は例2を参考にしていただければと思います。

例1. exists を使用しない場合

select t1.社員名
from 社員マスタ t1
where t1.社員ID = (
    select t2.部長ID
    from 部署マスタ t2
    where t2.部署名 = '営業'
        and t2.部長ID = t1.社員ID
);

例2. exists を使用する場合

select t1.社員名
from 社員マスタ t1
where exists (
    select *
    from 部署マスタ t2
    where t2.部署名 = '営業'
        and t2.部長ID = t1.社員ID
);

例1、例2共に7行目がポイントです。
副問合せ内(内枠)の条件式にて、社員マスタの項目「(t1.)社員ID」が指定されています。
そして、社員マスタは、(副問合せ内でなく、)外枠のselect文 (2行目のfrom句) で指定されているので「相関副問合せ」になります。
留意して欲しいことですが、”「相関副問合せ」を書くときは、テーブル名のエイリアスを指定しましょう。
副問合せ内にて、複数のテーブルの項目が混在する形になるので、可読性やエラー発生のリスクが高いためです。
(テーブルの結合時も同様ですね。)

最後に

今回はSQLの応用である「副問合せ」について説明しました。
「副問合せ」が書けるようになると、どんな複雑な抽出にも対応できるようになります。
ただ、「副問合せ」を多用すると、”可読性が下がる“という懸念点があります。
上記で書いたコード例は、比較的シンプルな抽出ですが、実業務で抽出する際は遥かに複雑になるでしょう。
「副問合せ」を身につけると、「複雑な抽出でも、とにかく副問合せを多用すれば抽出できる」と感じると思いますが、多用すると非常に読みずらくなります。
チームメンバーは勿論、書いた自分も、振り返った時に何を書いてるか分からなくなります。
「副問合せ」を使用する際は上記の点に注意しましょう。
また、副問合せを書く代わりに、with句を使う手があります。
本記事では説明対象外ですが、特に複雑な抽出の場合は、with句を使用する方が上記の点で良いです。

By clear

データエンジニア・機械学習・分析等を主とし、Webアプリ開発も行っているフリーランスです。