PostgreSQLでクエリを書いていると、「サブクエリが複雑で読みづらい」「共通処理を使いまわしたい」と感じたことはありませんか?
そんなときに便利なのがWITH句(共通テーブル式 / CTE)です。
WITH句を使えば、一時的なテーブルを定義して、その後のクエリ内で再利用することができるため、SQLの可読性と保守性を大きく向上させることができます。
この記事では、PostgreSQLにおけるWITH句の基本的な使い方から、再帰的なCTEの応用まで、具体的なサンプルコードとともにわかりやすく解説していきます。
PostgreSQL初心者の方でも理解できるよう、丁寧に解説していきますので、ぜひ最後まで読んでみてください。
初めてITに触れる方々のために、学習を助ける良書もご紹介しております。もしご興味があれば、そちらにも目を通していただければ嬉しく思います。
WITH句とは?
WITH句(共通テーブル式 / CTE: Common Table Expression) とは、クエリの先頭にサブクエリを定義し、それを名前付きの一時テーブルのように扱うことができる構文です。
通常、複雑なSQLではサブクエリを何度も書いたり、ネストが深くなったりして読みづらくなることがあります。そんなとき、WITH句を使うことでサブクエリを独立して定義し、クエリの中で使い回すことができます。
基本構文
WITH テーブル名 AS (
サブクエリ
)
SELECT ... FROM テーブル名;
このように、WITH
のあとに一時的なテーブル名とサブクエリを定義し、メインのSELECT文でそのテーブル名を参照する形になります。
基本的な使い方
単純なサブクエリの置き換え
まずは、サブクエリをWITH句に置き換える基本的な使い方を紹介します。
例:特定期間の売上合計を求める
WITH sales_summary AS (
SELECT
customer_id,
SUM(amount) AS total_sales
FROM sales
WHERE sale_date BETWEEN '2024-01-01' AND '2024-12-31'
GROUP BY customer_id
)
SELECT
customer_id,
total_sales
FROM sales_summary
WHERE total_sales > 10000;
この例では、特定期間の売上合計を一度まとめたうえで、合計金額が10,000を超える顧客だけを抽出しています。
元々サブクエリとして埋め込むこともできますが、WITH句にすることでロジックの切り分けが明確になり、読みやすく・再利用しやすいSQLになります。
複数のWITH句を使う
PostgreSQLでは、複数のWITH句(CTE)を定義して、順に使うことができます。これにより、段階的にデータを処理したいケースや、処理をステップごとに分けたいときに非常に便利です。
例:売上データと顧客データを組み合わせて抽出
WITH sales_summary AS (
SELECT
customer_id,
SUM(amount) AS total_sales
FROM sales
GROUP BY customer_id
),
high_sales AS (
SELECT
customer_id,
total_sales
FROM sales_summary
WHERE total_sales > 10000
)
SELECT
h.customer_id,
h.total_sales,
c.customer_name
FROM high_sales h
JOIN customers c ON h.customer_id = c.customer_id;
このクエリでは、以下の2段階に分けて処理を行っています。
sales_summary
で顧客ごとの合計売上を算出high_sales
で売上が10,000を超える顧客を抽出し、顧客情報と結合
複雑になりがちな集計と結合の処理も、WITH句を使えばステップごとに明確に記述できるようになります。
再帰的なWITH句(再帰CTE)
WITH句の中でも特に強力な機能が、「再帰的CTE」です。階層構造のデータ(カテゴリ構造、組織図、フォルダ階層など)を扱う際に活躍します。
PostgreSQLでは、再帰的なCTEを使って親子関係のデータを再帰的にたどって展開することができます。
再帰CTEの基本構文
WITH RECURSIVE cte名 AS (
-- 基本ケース(初期データ)
SELECT ...
FROM ...
WHERE 条件
UNION ALL
-- 再帰ケース(自己結合)
SELECT ...
FROM 元テーブル
JOIN cte名 ON ...
)
SELECT * FROM cte名;
例:カテゴリ階層の展開
WITH RECURSIVE category_tree AS (
SELECT
id,
name,
parent_id,
1 AS level
FROM categories
WHERE parent_id IS NULL
UNION ALL
SELECT
c.id,
c.name,
c.parent_id,
ct.level + 1
FROM categories c
JOIN category_tree ct ON c.parent_id = ct.id
)
SELECT * FROM category_tree ORDER BY level;
このクエリでは、categories
テーブルから親カテゴリ → 子カテゴリ → 孫カテゴリ… と再帰的にたどって全階層を取得しています。
まとめ
この記事では、PostgreSQLにおけるWITH句(共通テーブル式 / CTE)の基本から応用までを解説しました。
- WITH句は、複雑なクエリを分かりやすく整理するために非常に便利な構文です。
- サブクエリの代わりとして使うことで、SQLの可読性・再利用性が向上します。
- 複数のCTEや再帰CTEを使えば、より柔軟で強力なデータ処理が可能になります。
- 階層構造の展開や段階的な集計処理など、実務でも役立つ場面が多くあります。
PostgreSQLでのSQLを書く上で、WITH句を使いこなせるようになると、データベース操作の幅がグッと広がります。
最初は少しとっつきにくく感じるかもしれませんが、ぜひ積極的に使ってみてください!
もしこの内容を通して、PostgreSQLについてさらに理解を深めたいと感じられたなら、信頼できる講座や書籍を紹介した別記事をご覧いただくのも良いかと思います。ご自身の学びに、きっとお役立ていただけるはずです。
コメント