SQLの「WITH句」について、わかりやすく解説します。大量データを扱ったり、複雑な副問合せが入り組んだクエリを書いたりする際に知っておくと、とても役に立ちます。
WITH句とは?
WITH句とは、一時的な名前付きの副問合せを定義する仕組みです。通常の副問合せと比べて、下記のようなメリットがあります。
再利用が簡単
同じ副問合せを何度も繰り返し書かなくていいため、クエリ全体の重複を大幅に減らすことができます。
可読性が向上
複数の副問合せをそれぞれWITH句に切り出すことで役割が明確になり、長いクエリでも構造を把握しやすくなります。
再帰的な問い合わせが可能
WITH RECURSIVE を使うと、階層構造やツリー構造を再帰的に取得できるようになります。従来はテーブルを自己結合する手間が多かったケースも、スッキリと記述できます。
基本的なWITH句の構文
WITH サブクエリ名 AS (
SELECT
カラム1,
カラム2
FROM
テーブルA
WHERE
カラム1 > 100
)
SELECT
サブクエリ名.カラム1,
サブクエリ名.カラム2
FROM
サブクエリ名;
WITH サブクエリ名 AS (…) で定義した副問合せを、あたかもテーブルのようにメインのSELECTで利用できます。
サンプルケース
WITH adults AS (
SELECT
user_id,
name,
age
FROM
users
WHERE
age >= 20
)
SELECT
user_id,
name,
age
FROM
adults;
上記では、adults という副問合せ(テーブルのようなもの)で「20歳以上のユーザー情報」をまとめています。
従来の副問合せをわざわざネストしなくてもよいので、可読性・保守性が上がります。
複数の名前付き副問合せを定義する
WITH句では、カンマ区切りでいくつでも定義できます。例えば、段階的にデータを絞り込んだり、異なる条件で抽出した結果を結合したりする場合に便利です。
WITH
condition_a AS (
SELECT ...
FROM ...
WHERE ...
),
condition_b AS (
SELECT ...
FROM ...
WHERE ...
)
SELECT
...
FROM
condition_a
JOIN
condition_b
ON condition_a.xxx = condition_b.xxx;
先に condition_a を定義し、次に condition_b を定義――というように、ステップごとにロジックを分割して書けます。
読み手は「最初にどんなデータを作っているか」「最後にどう利用しているか」を簡単に理解できるので、SQLのメンテナンスが楽になります。
再帰的な副問合せで階層構造を取得する
WITH RECURSIVE を使用すると、自己参照テーブルにおける親子関係やツリー構造を再帰的に辿って取得できます。これは従来の自己結合を繰り返す手法よりもシンプルで読みやすいです。
テーブル例
employee_id | name | manager_id |
---|---|---|
1 | Alice | NULL |
2 | Bob | 1 |
3 | Charlie | 1 |
4 | Diana | 2 |
5 | Eric | 2 |
6 | Fumiko | 3 |
manager_id は、上司の employee_id を参照しています。Alice は最上位のため manager_id がNULL。
WITH RECURSIVEを使った例
WITH RECURSIVE employee_hierarchy AS (
-- (1) 階層のスタート: manager_id が NULL の社員
SELECT
employee_id,
name,
manager_id,
1 AS level
FROM employees
WHERE manager_id IS NULL
UNION ALL
-- (2) 再帰呼び出し: 直前で取得した社員を上司にもつ社員を取得
SELECT
e.employee_id,
e.name,
e.manager_id,
eh.level + 1 AS level
FROM employees e
JOIN employee_hierarchy eh
ON e.manager_id = eh.employee_id
)
SELECT
employee_id,
name,
manager_id,
level
FROM
employee_hierarchy
ORDER BY
level,
employee_id;
初回のSELECTで「トップレベル(上司不在)の社員」を取り出し、UNION ALLの後半で「その社員を上司に持つ部下」を再帰的に取得しています。
eh.level + 1 AS level のように階層の深さをインクリメントすると、どの階層にいるのかが一目瞭然になります。
まとめ
副問合せを多用して複雑になっているSQLがあれば、ぜひこのWITH句を活用してみてください。クエリが見通し良くなり、今までよりもスムーズに管理できるはずです。