WITH 句(共通表式・CTE)の使い方
shain元の表
| shain_id | name | salary |
|---|---|---|
| 1 | 鈴木 | 980000 |
| 2 | 佐藤 | 720000 |
| 3 | 田中 | 520000 |
| … | ||
heikinWITH で作った表
| h |
|---|
| 542003 |
結果平均より上の人
| name | salary |
|---|---|
| 鈴木 | 980000 |
| 佐藤 | 720000 |
| 山田 | 700000 |
| … | |
- 元の表 18 行 → WITH で作った表 1 行 → 平均より上の人 5 行
- 前の段の結果が次の段の入力になります。表が 1 つ増えたつもりで読んでください
真ん中の heikin が WITH で作った表です。1 行 1 列しかありませんが、これも立派な表として本体から参照できます。
WITH は問い合わせに名前を付けて、そのあとの本体から表として使う書き方です。 共通表式(CTE)とも呼びます。同じ集計を 2 か所で使うとき、段階を分けて上から読めるようにするときに効きます。
主要 7 製品で通用 そのまま使える: PostgreSQL / MySQL / MariaDB / SQLite / SQL Server / Oracle Database / IBM Db2
このページの実行結果と図は、すべて本物のデータベースに同じ SQL を流して得たものです。手で書いた「たぶんこうなる」ではありません。
01先に名前を付けてから本体を書く
結論: WITH 名前 AS (問い合わせ) 本体 の形です。 本体からは、その名前をふつうの表のように使えます。
読む順番が「材料 → 本体」になるのが特徴です。派生表は FROM の中に問い合わせが入り込むので、外側を読むために内側を先に読む必要がありますが、WITH なら上から順に読めます。
名前はカンマで区切っていくつでも定義できます。あとの定義から前の定義を参照することもできます。
⚠️ WITH で作った表は、その文の中だけのものです。文が終われば消えるので、表を作る(CREATE TABLE)のとは違います。
WITH zaiseki AS (★ここで名前を付けるSELECT * FROM shain中身はふつうの問い合わせWHERE taishoku_on IS NULL)在籍中だけSELECT b.busho_name, count(*) AS ninzu★ここから本体FROM zaiseki z付けた名前を表として使うJOIN busho b ON b.busho_id = z.busho_id結合の相手にもできるGROUP BY b.busho_nameまとめるORDER BY count(*) DESC, b.busho_name;結果の並べ方
上から読めば「在籍中の人を zaiseki と呼ぶ。その zaiseki を部署ごとに数える」と、そのまま日本語になります。
WITH zaiseki AS (
SELECT * FROM shain WHERE taishoku_on IS NULL
)
SELECT b.busho_name, count(*) AS ninzu
FROM zaiseki z
JOIN busho b ON b.busho_id = z.busho_id
GROUP BY b.busho_name
ORDER BY count(*) DESC, b.busho_name;| busho_name | ninzu |
|---|---|
| 開発 | 4 |
| 営業 | 3 |
| マーケ | 2 |
| 基盤 | 2 |
| 管理 | 2 |
| 経営 | 1 |
(6 行)
6 行です。派生表で書いた場合とまったく同じ結果で、違うのは読む順番だけです。
02同じ結果を 2 か所で使える
結論: WITH の本当の効きどころは、同じ結果を 2 回以上使うときです。
派生表は書いた場所でしか使えないので、2 か所で必要なら同じ SQL を 2 回書くことになります。片方だけ直して食い違う事故が起きます。
WITH なら定義は 1 か所です。直す場所が 1 つなので、食い違いようがありません。
⚠️ 「2 回書いてあるのは同じものか」を読む人に確かめさせないで済む、というのが本質です。速度の話ではありません。
WITH heikin AS (
SELECT round(avg(salary)) AS h FROM shain
)
SELECT (SELECT h FROM heikin) AS zentai_heikin,
count(*) AS ue_no_ninzu
FROM shain
WHERE salary > (SELECT h FROM heikin);| zentai_heikin | ue_no_ninzu |
|---|---|
| 542003 | 5 |
(1 行)
平均 542003 円を上回るのは 5 人でした。平均を出す式は 1 回しか書いていないので、片方だけ直してずれる心配がありません。
⚠️ この書き方は SQL Server / IBM Db2 では使えません(round(x)(引数 1 つ))。製品ごとの対応表
03段階を分けて上から読めるようにする
結論: 名前をいくつも定義して、処理を段に分けられます。
1 つの SELECT に副問い合わせを何重にも入れると、内側から読まないと分からない文になります。WITH で段に分ければ、上から順に「これを作って、次にこれを作って、最後にこう出す」と読めます。
名前の付け方がそのまま説明になるので、コメントを書かなくても意図が伝わるのが利点です。
⚠️ 分けすぎると今度は行き来が増えます。1 つの段が 1 つの意味になるところで切ってください。
WITH zaiseki AS (
SELECT * FROM shain WHERE taishoku_on IS NULL
),
busho_goukei AS (
SELECT busho_id, sum(salary) AS goukei, count(*) AS ninzu
FROM zaiseki
GROUP BY busho_id
)
SELECT b.busho_name, g.ninzu, g.goukei
FROM busho_goukei g
JOIN busho b ON b.busho_id = g.busho_id
ORDER BY g.goukei DESC;| busho_name | ninzu | goukei |
|---|---|---|
| 開発 | 4 | 2215050 |
| 営業 | 3 | 1651000 |
| 管理 | 2 | 1105000 |
| 基盤 | 2 | 1078000 |
| 経営 | 1 | 980000 |
| マーケ | 2 | 903000 |
(6 行)
6 行です。あとの定義(busho_goukei)から前の定義(zaiseki)を参照しています。上から順に読めば、何をしているかが名前だけで分かります。
04派生表とどちらを使うか
結論: 1 か所でしか使わないなら派生表、2 か所以上か段に分けたいなら WITH が目安です。
どちらでも書けることがほとんどなので、読みやすいほうを選んでください。
- 派生表 … その場に書いてある。短い処理なら目で追いやすい
WITH… 名前が付く。長い処理や、同じものを何度も使う場合に強い
⚠️ 速さは製品と状況によります。 「WITH は遅い」「派生表のほうが速い」といった一般論は、いまの製品では当てになりません。遅いと感じたら両方書いて実際に測ってください。
⚠️ WITH には再帰という別の使い方もあります。階層をたどるための書き方で、こちらは派生表では代用できません。
SELECT b.busho_name, count(*) AS ninzu
FROM (SELECT * FROM shain WHERE taishoku_on IS NULL) t
JOIN busho b ON b.busho_id = t.busho_id
GROUP BY b.busho_name
ORDER BY count(*) DESC, b.busho_name;| busho_name | ninzu |
|---|---|
| 開発 | 4 |
| 営業 | 3 |
| マーケ | 2 |
| 基盤 | 2 |
| 管理 | 2 |
| 経営 | 1 |
(6 行)
6 行で、WITH で書いた最初の例と完全に同じ結果です。違うのは名前が付いているかどうかだけです。
自分で打ってみる
このページの例と同じデータが入った本物のデータベースを、この場(あなたのブラウザの中)で起動できます。打った SQL がサーバーへ送られることはありません。
押すと初回だけ約 9MB を読み込みます(2 回目以降はキャッシュされます)。
解いてみる
読んだだけでは書けるようになりません。実行結果で採点します。
関連するトピック
- FROM 句の副問い合わせFROM の中に直接書く書き方です。
- 再帰 CTEWITH で階層をたどる書き方です。
- GROUP BY 句段の中でよく使うまとめる操作です。
- スカラー副問い合わせ値として使う副問い合わせはこちらです。
根拠(一次情報)
- PostgreSQL 18 マニュアル: WITH 句(共通表式)一次情報・確認 2026-08-13
- PostgreSQL 18 マニュアル: SELECT(WITH の位置)一次情報・確認 2026-08-13