e2.department_id = e.department_id の条件は、外側のクエリの別名 e を参照します。
-- Fetch employees earning more than the average salary in deptSELECT
e.employee_id,
e.employee_name,
e.salary,
e.department_id
FROM employees e
WHERE e.salary >(SELECTAVG(e2.salary)-- Calculate the average salaryFROM employees e2
WHERE e2.department_id = e.department_id
-- Correlation: references the outer query's department_id);提供:
-- Fetch customers with at least one orderSELECT
c.customer_id,
c.customer_name
FROM customers c
WHEREEXISTS(SELECT1FROM orders o
WHERE o.customer_id = c.customer_id
-- Correlation: references the outer query customer_id);提供:
-- Query employees who earn more than the overall average salarySELECT
employee_id,
employee_name,
salary
FROM employees
WHERE salary >(SELECTAVG(salary)FROM employees
);提供:
-- Use subquery to fetch employees earning more than the average salary in deptSELECT
e.employee_id,
e.employee_name,
e.salary,
e.department_id
FROM employees e
WHERE e.salary >(SELECTAVG(e2.salary)FROM employees e2
WHERE e2.department_id = e.department_id
);提供:
同じ結果を得るために、JOIN 句を用いてクエリを書き換えることができます。
-- Use JOIN to fetch employees earning more than the average salary in deptSELECT
e.employee_id,
e.employee_name,
e.salary,
e.department_id
FROM employees e
JOIN(SELECT
department_id,AVG(salary)AS avg_salary
FROM employees
GROUPBY department_id
-- Precompute the department average once per department) dept_avg
ON e.department_id = dept_avg.department_id
-- Match employees with their department averagesWHERE e.salary > dept_avg.avg_salary;-- Compare salary with the computed department average提供:
以下の表は、SQLにおける相関サブクエリとJOINの違いをまとめたものです。
項目
相関サブクエリ
JOIN
可読性
ロジックを WHERE 句で直接表現できるため、読みやすいことが多い。
派生テーブルやCTEを要する場合があり、やや複雑になることがある。
ロジックの表現
条件を自然に表現できる。例:「部署平均より給与が高い」。
まず集約値を計算し、その後メインのテーブルに結合する必要がある。
実行動作
サブクエリは外側のクエリの各行に対して実行される場合がある。
集約結果は通常一度計算され、再利用される。
パフォーマンス
繰り返し実行されるため、大規模データでは遅くなる可能性がある。
大規模テーブルでは通常より効率的。
主なユースケース
EXISTS() による存在確認や、行単位の集約との比較など、行固有の条件チェック。
レポーティング・クエリ、集約、パフォーマンス重視のワークロード。
おすすめとして、Joining Data in SQL コースで、SQLにおけるさまざまなJOINの種類や、データベース内の関連テーブルの扱い方を学んでください。
相関サブクエリ vs. ウィンドウ関数
近年のSQLでは、ウィンドウ関数(AVG() や OVER (PARTITION BY) など)を使えば、1回の走査で行ごとの集約を計算できます。
例えば次のクエリは、所属部署の平均給与より高い給与の従業員を返します。サブクエリ内で OVER () によって集約をウィンドウ関数に変換し、PARTITION BY department_id でテーブルを部署ごとのグループ(パーティション)に分割しています。
-- Use window function to get employees earning more than dept average salarySELECT
employee_id,
employee_name,
salary,
department_id
FROM(SELECT
employee_id,
employee_name,
salary,
department_id,AVG(salary)OVER(PARTITIONBY department_id)AS dept_avg_salary
-- Window function calculates department average once per partitionFROM employees
) t
WHERE salary > dept_avg_salary;提供:
ただし、EXISTS() や NOT EXISTS() を使ってテーブル間の関係を検証したい場合には、相関サブクエリは依然として有用です。ウィンドウ関数が使えないデータベースや状況でも相関サブクエリを用いることがあります。
リレーショナル・データベースでは、行同士が相互に依存していることが多く、複雑な問いに答えるには、現在処理しているテーブルにクエリが立ち戻る必要が生じることがあります。
このようなテーブルを照会するために、SQLでは相関サブクエリが利用できます。これは、内側のクエリが外側のクエリの値に依存するという特定の関係を定義するものです。標準的なサブクエリが一度だけ実行されて終了するのに対し、相関サブクエリは動的で、メインクエリが評価する各行ごとに繰り返し実行されます。
本チュートリアルでは、SQLにおける相関サブクエリの仕組み、パフォーマンス上の考慮点、そしてJOINやウィンドウ関数と比較してどのような場面で適切かを解説します。SQLが初めての場合は、まず Introduction to SQL コースから始めてください。ある程度の経験がある場合は Intermediate SQL コースをご覧ください。
相関サブクエリとは?
相関サブクエリは、実行にあたって外側のクエリの値に依存するタイプのサブクエリです。
固定の結果を一度返して終わるのではなく、外側のクエリで処理される各行ごとに内側のクエリが評価されます。これは、内側のクエリが外側のクエリの列を参照しており、両者の間に直接的な結びつきがあるために起こります。
これに対し、非相関サブクエリは外側のクエリとは独立して実行されます。一度実行して結果セットまたは値を返し、外側のクエリはその結果を各行でサブクエリを再実行することなく利用します。
相関サブクエリの動作
SQLにおける典型的な相関サブクエリのワークフローは次のとおりです。
相関サブクエリの仕組み。画像提供:Gemini。
SQLにおける相関サブクエリの例
ここまでの説明は概念的なものです。学ぶ最良の方法は、例を通して手を動かすことです。
例1:部署平均を上回る給与の従業員
employeesテーブルに従業員の給与と部署IDがあるとします。各部署の平均給与よりも多く稼いでいる従業員を見つけたいとします。次のクエリを使用します。ここで:
外側のクエリは
employeesテーブルから従業員を選択します。サブクエリは同じ部署の平均給与を計算します。
e2.department_id = e.department_idの条件は、外側のクエリの別名eを参照します。例2:相関サブクエリとEXISTS()の併用
相関サブクエリに EXISTS() 演算子を組み合わせて、別のテーブルに関連レコードが存在するかどうかを確認することもできます。
たとえば、
customersとordersテーブルがあるとします。少なくとも1回注文を行った顧客を一覧表示したい場合、次のクエリを用います。ここで:外側のクエリは
customersテーブルの行を走査します。サブクエリは、その顧客に対して少なくとも1件の注文が存在するかを確認します。
o.customer_id = c.customer_idの条件が、サブクエリを外側のクエリに結びつけます。上記のクエリでは、SQLはordersテーブルに一致する行が存在するかどうかを確認します。存在すれば
EXISTS()演算子は true を返し、その顧客は結果に含まれます。相関サブクエリと非相関サブクエリの比較
先に見たとおり、SQLのサブクエリは非相関サブクエリと相関サブクエリのいずれかに分類されます。内側のクエリが外側のクエリに依存するかどうかが重要な違いです。
非相関サブクエリでは、データベースはそれを一度実行し、その結果を外側のクエリで利用します。
たとえば、次のクエリは全体の平均給与より多く稼いでいる従業員を見つけます。
上記のクエリでは、サブクエリがテーブル全体の平均給与を計算し、これが1回だけ実行されます。外側のクエリは各従業員の給与をその1つの値と比較します。
非相関サブクエリは一度だけ実行されるため、結果を使い回せる場合は通常より高速です。全体平均や合計などのグローバルな比較に最適です。
一方で、相関サブクエリは大規模テーブルでは遅くなることがあります。部署単位の比較や存在確認のように、各行に相対的な条件を評価する必要がある場合に有用です。
さらに詳しく学ぶには、 Introduction to SQL Server コースを受講し、グルーピングと集約、テーブルの結合について理解を深めてください。
相関サブクエリ vs. JOIN
多くの相関サブクエリはJOIN を使って書き換えることができます。リレーショナル・データベースでは、JOINは行ごとではなく集合単位で関係を処理できるため、より高いパフォーマンスを発揮します。
次の相関サブクエリを使ったクエリを考えます。これは各部署の平均給与を上回って支払われている従業員を一覧表示します。
同じ結果を得るために、
JOIN句を用いてクエリを書き換えることができます。以下の表は、SQLにおける相関サブクエリとJOINの違いをまとめたものです。
項目
相関サブクエリ
JOIN
可読性
ロジックを
WHERE句で直接表現できるため、読みやすいことが多い。派生テーブルやCTEを要する場合があり、やや複雑になることがある。
ロジックの表現
条件を自然に表現できる。例:「部署平均より給与が高い」。
まず集約値を計算し、その後メインのテーブルに結合する必要がある。
実行動作
サブクエリは外側のクエリの各行に対して実行される場合がある。
集約結果は通常一度計算され、再利用される。
パフォーマンス
繰り返し実行されるため、大規模データでは遅くなる可能性がある。
大規模テーブルでは通常より効率的。
主なユースケース
EXISTS()による存在確認や、行単位の集約との比較など、行固有の条件チェック。レポーティング・クエリ、集約、パフォーマンス重視のワークロード。
おすすめとして、Joining Data in SQL コースで、SQLにおけるさまざまなJOINの種類や、データベース内の関連テーブルの扱い方を学んでください。
相関サブクエリ vs. ウィンドウ関数
近年のSQLでは、ウィンドウ関数(
AVG()やOVER (PARTITION BY)など)を使えば、1回の走査で行ごとの集約を計算できます。例えば次のクエリは、所属部署の平均給与より高い給与の従業員を返します。サブクエリ内で
OVER ()によって集約をウィンドウ関数に変換し、PARTITION BY department_idでテーブルを部署ごとのグループ(パーティション)に分割しています。ただし、
EXISTS()やNOT EXISTS()を使ってテーブル間の関係を検証したい場合には、相関サブクエリは依然として有用です。ウィンドウ関数が使えないデータベースや状況でも相関サブクエリを用いることがあります。相関サブクエリのパフォーマンス
相関サブクエリは強力ですが、しばしばパフォーマンス面での課題を伴います。
相関サブクエリは繰り返し実行される
外側のクエリの各行ごとに実行されるため、内側のデータを何度も再走査してしまい、大規模テーブルではクエリが遅くなる場合があります。外側のテーブルが10万行あるなら、データベースは10万回のサブタスクを実行することになります。
ボトルネック
適切に最適化しないと、相関クエリはCPU使用率の上昇や長い待ち時間を招くことがあります。特に、内側のクエリが複雑な計算を行ったり、大きなテーブルを走査している場合に顕著です。
列へのインデックス付与
相関に用いる列にインデックスを付けると、データベースはサブクエリ内の関連行をテーブル全体を毎回走査することなく、ほぼ即座に見つけられるようになります。
クエリプランナの最適化
最新のデータベースは、内部で相関サブクエリを最適化することがよくあります。クエリプランナは、JOINやキャッシュされた集約など、より効率的な形にクエリを変換し、実行時間を大幅に短縮することがあります。
相関サブクエリを使うべき場面
次のような場合に相関サブクエリを使用できます。
行固有の集約に基づくフィルタリング: 各行に相対的な値を比較する必要がある場合に使用します(例:部署平均を上回る給与の従業員)。
EXISTS()で関連データを確認:
EXISTS()と組み合わせて、関連行の有無を検証できます。複雑な入れ子のロジックの表現: 複雑な条件を、長い
JOINの連鎖よりも読みやすく表現できることがあります。ただし、次のような場合は相関サブクエリの使用を避けてください。
単純なJOINで済む:
LEFT JOINやINNER JOINで同じ結果が得られるなら、常にそちらの方が高速です。ビッグデータを扱う: 相関条件がインデックスのない大きなテーブルを参照する場合、繰り返しの評価によりクエリが大幅に遅くなることがあります。
相関サブクエリのよくあるミス
相関サブクエリを使用する際によく遭遇する問題と、その対処法は次のとおりです。
まとめ
相関サブクエリを使う場面と使い方、そして他の手法に置き換えるべき場面を見極めることは、明快で効率的なSQLを書くうえで重要なスキルです。
次のステップとして、SQL Associate Certification の取得をおすすめします。SQLを用いたデータ分析のスキルを証明し、他のデータプロフェッショナルの中で差別化できます。最後に、Database Design コースでは、データベースの作成・管理方法や、要件に合ったDBMSの選定について学べます。