SQLサブクエリ入門:ネストしたSELECT文で複雑なデータ抽出をマスターしよう
SQLを学習していると、時折「もっと複雑な条件でデータを絞り込めないか?」「複数のテーブルから情報を組み合わせるだけでなく、ある条件を満たすデータだけを対象にしたい」といった疑問にぶつかることがあります。
そんな時、非常に強力な武器となるのが「サブクエリ(副問い合わせ)」です。
サブクエリとは、一言で言えば「SELECT文の中にさらにSELECT文を埋め込む」テクニックのこと。
これだけ聞くと難しそうに感じるかもしれませんが、基本的な考え方は非常にシンプルです。
この記事では、SQLのサブクエリの基本から、具体的な使い方、そしてそのメリット・デメリットまでを、初心者の方にも分かりやすく解説していきます。
サブクエリとは何か? 基本の「キ」
サブクエリは、英語では "subquery" や "inner query" と呼ばれます。
これは、あるSQL文(外側のクエリ、またはメインクエリと呼ばれます)の実行結果をさらに絞り込んだり、別の条件で参照したりするために使われる、入れ子になったSELECT文のことです。
例えば、「注文履歴の中から、総注文金額が10000円以上の顧客だけを抽出したい」という場合を考えてみましょう。
いきなりこれを一つのSQL文で書こうとすると、少し複雑になりがちです。
しかし、サブクエリを使えば、まず「顧客ごとの総注文金額を計算するSELECT文」を作り、その結果を使って「総注文金額が10000円以上の顧客を絞り込むSELECT文」を作成する、というように段階的に処理を進めることができます。
サブクエリの主な使いどころ
サブクエリは、主に以下のような場面で活躍します。
- WHERE句での利用: サブクエリの結果を条件として、メインクエリのデータを絞り込む。
- FROM句での利用: サブクエリの結果を一時的なテーブル(派生テーブル)として扱い、そのテーブルに対してさらに操作を行う。
- SELECT句での利用: メインクエリの各行に対して、サブクエリの結果を計算して表示する。
今回は、最も一般的で理解しやすい「WHERE句での利用」を中心に見ていきましょう。
WHERE句でのサブクエリ:具体的な例を見てみよう
では、具体的なテーブル構成を想定して、サブクエリを使ったSQL文を見てみましょう。
以下のような orders テーブルがあるとします。
CREATE TABLE orders (
order_id INTEGER PRIMARY KEY,
customer_id INTEGER,
order_date DATE,
amount REAL
);
INSERT INTO orders (order_id, customer_id, order_date, amount) VALUES
(1, 101, '2023-01-15', 5000),
(2, 102, '2023-01-20', 12000),
(3, 101, '2023-02-10', 7000),
(4, 103, '2023-02-15', 3000),
(5, 102, '2023-03-05', 15000),
(6, 101, '2023-03-10', 4000);
このテーブルから、「総注文金額が10000円以上の顧客の customer_id をすべて取得する」というクエリを考えてみます。
まず、顧客ごとの総注文金額を計算するサブクエリを作成します。
SELECT customer_id, SUM(amount) AS total_amount
FROM orders
GROUP BY customer_id;
このサブクエリを実行すると、以下のような結果が得られます。
| customer_id | total_amount |
|---|---|
| 101 | 16000 |
| 102 | 27000 |
| 103 | 3000 |
この結果から、total_amount が10000以上の customer_id を抽出したいわけです。
これをメインクエリと組み合わせると、以下のようになります。
SELECT DISTINCT customer_id
FROM orders
WHERE customer_id IN (
SELECT customer_id
FROM orders
GROUP BY customer_id
HAVING SUM(amount) >= 10000
);
このSQL文を分解して見てみましょう。
-
内側のSELECT文(サブクエリ):
SELECT customer_id FROM orders GROUP BY customer_id HAVING SUM(amount) >= 10000これは、
ordersテーブルから、顧客ごとにグループ化し、その合計金額 (SUM(amount)) が10000以上であるcustomer_idを抽出します。 このサブクエリの結果は、customer_idのリスト(例:(101, 102))になります。 -
外側のSELECT文(メインクエリ):
SELECT DISTINCT customer_id FROM orders WHERE customer_id IN (...)これは、
ordersテーブル全体から、customer_idがサブクエリで返されたリストに含まれるもの (IN演算子を使用) をすべて取得します。DISTINCTをつけているのは、同じ顧客が複数回登場する可能性があるため、ユニークなcustomer_idだけを取得するためです。
このSQLを実行すると、以下のような結果が得られます。
| customer_id |
|---|
| 101 |
| 102 |
このように、サブクエリを使うことで、複雑な条件を段階的に満たしていくことが可能になります。
サブクエリのメリット・デメリット
サブクエリは非常に便利な機能ですが、万能ではありません。
そのメリットとデメリットを理解しておくことが、より効率的なSQL記述につながります。
メリット
- 可読性の向上: 複雑な条件をサブクエリに切り出すことで、メインクエリがシンプルになり、SQL文全体の意図が理解しやすくなることがあります。
- 柔軟なデータ抽出: JOINだけでは実現が難しい、あるいは非常に複雑になるような条件でのデータ抽出が可能になります。
- 段階的な処理: 複数のステップに分けてデータを処理できるため、論理的な思考をSQLに落とし込みやすいです。
デメリット
- パフォーマンス: サブクエリ、特にWHERE句で使われる相関サブクエリ(後述)は、実行回数が多くなる傾向があり、パフォーマンスに影響を与える可能性があります。 データベースによっては、JOINを使った方が高速に処理できる場合も多いです。
- 複雑化の可能性: サブクエリを多用しすぎると、逆にSQL文が読みにくく、メンテナンスしづらくなることもあります。
パフォーマンスについては、データベースの種類やバージョン、データ量、インデックスの有無などによって大きく変わるため、実際にクエリを実行してみて、パフォーマンスチューニングを検討することが重要です。
その他のサブクエリの形
ここまでWHERE句でのサブクエリを見てきましたが、サブクエリは他にもいくつかの形で利用できます。
FROM句でのサブクエリ(派生テーブル)
サブクエリの結果を、あたかも一時的なテーブルのように扱って、さらにSQL操作を行う方法です。
例えば、先ほどの例で、総注文金額が10000円以上の顧客の「顧客ID」と「総注文金額」を両方取得したい場合、以下のように書けます。
SELECT customer_id, total_amount
FROM (
SELECT customer_id, SUM(amount) AS total_amount
FROM orders
GROUP BY customer_id
HAVING SUM(amount) >= 10000
) AS high_value_customers;
この場合、括弧内のSELECT文が一時的なテーブル high_value_customers として扱われます。
SELECT句でのサブクエリ(スカラーサブクエリ)
SELECT句の中に書かれるサブクエリで、単一の値を返すものを「スカラーサブクエリ」と呼びます。
例えば、各注文に対して、その注文がその顧客の全注文の中で何番目かを計算して表示したい場合などに使われます。
SELECT
o.order_id,
o.customer_id,
o.amount,
(
SELECT COUNT(*)
FROM orders AS o2
WHERE o2.customer_id = o.customer_id
AND o2.order_date < o.order_date
) + 1 AS order_sequence_number
FROM orders AS o;
この例では、外側のクエリの各行 (o) に対して、内側のサブクエリが実行され、その顧客におけるその注文より前の注文数を数え、+1 することで順序を求めています。
これは「相関サブクエリ」と呼ばれるもので、外側のクエリの各行の値に依存して実行されるため、パフォーマンスには注意が必要です。
まとめ
サブクエリは、SQLで複雑なデータ抽出を行うための強力なツールです。
- SELECT文の中に別のSELECT文を埋め込むことで、条件を段階的に絞り込んだり、参照したりできます。
- WHERE句、FROM句、SELECT句など、様々な場所で利用できます。
- 可読性や柔軟性が向上する一方で、パフォーマンスに影響を与える可能性もあるため、注意が必要です。
今回紹介したサブクエリの基本的な使い方をマスターすれば、より高度なデータ分析への道が開けます。
ぜひ、様々なデータで試してみてください。