Kodokon kodokon.com

CTE(WITH):複雑なクエリを読みやすくする

WITHでクエリを名前の付いたステップに構造化し、変換のパイプラインのようにCTEを連鎖させましょう。

7 分 · 3 問

このレッスンを Kodokon で開く

三段階の深さに入れ子になったサブクエリは、内側から外側へと読みます。読みにくく、半年後に見返すのも不可能です。CTE(Common Table Expression、共通テーブル式、WITH句)は読む順序を逆転させます。関数に名前を付けるのと同じように、各中間ステップに名前を付け、クエリは上から下へと読めます。このデータセットをsqliteonline.comまたはsqlite3に読み込みましょう。

SQL
CREATE TABLE payments (
  id INTEGER PRIMARY KEY,
  customer TEXT NOT NULL,
  paid_at TEXT NOT NULL,
  amount REAL NOT NULL
);

INSERT INTO payments (customer, paid_at, amount)
VALUES
  ('alice', '2026-01-10', 120.0),
  ('bruno', '2026-01-22', 80.0),
  ('alice', '2026-02-05', 60.0),
  ('chloe', '2026-02-18', 200.0),
  ('bruno', '2026-03-03', 40.0),
  ('alice', '2026-03-15', 90.0),
  ('chloe', '2026-03-28', 150.0),
  ('bruno', '2026-04-09', 300.0);
このレッスンのデータセット。

基本構文はWITH name AS (SELECT ...)に続けてメインクエリを書き、そこでnameを普通のテーブルのように使います。ここでは、substrで日付から月を取り出して月次の売上を計算します(SQLiteの日付はISO 8601のテキストで、標準というよりエンジンの選択です)。

SQL
WITH monthly AS (
  SELECT substr(paid_at, 1, 7) AS month,
         SUM(amount) AS revenue
  FROM payments
  GROUP BY month
)
SELECT * FROM monthly ORDER BY month;
シンプルなCTE:名前が付いた再利用可能な一つのステップ。

本当の力は連鎖から生まれます。一つのWITH、カンマで区切られた複数のCTE、それぞれが自分より前に定義されたものを読めます。パイプラインを組み立てるのです。集約し、統計を計算し、比較する。ここでは、売上が月次平均を超える月を探します(期待される結果:2026-03と2026-04、平均は260)。

SQL
WITH monthly AS (
  SELECT substr(paid_at, 1, 7) AS month,
         SUM(amount) AS revenue
  FROM payments
  GROUP BY month
),
stats AS (
  SELECT AVG(revenue) AS avg_revenue
  FROM monthly
)
SELECT m.month, m.revenue,
       ROUND(s.avg_revenue, 1) AS avg_all
FROM monthly AS m
CROSS JOIN stats AS s
WHERE m.revenue > s.avg_revenue
ORDER BY m.month;
連鎖したCTE:statsはmonthlyを読み、最後のSELECTは両方を読む。

理解度チェック

このレッスンの要点をしっかり覚えているか確認しましょう。

  1. 同じクエリで複数のCTEを宣言するにはどうしますか?
    • 各CTEの前にWITHキーワードを置く
    • 一つのWITH、CTEはカンマで区切る
    • 互いに入れ子にする
  2. CTEは同じWITHの別のCTEを読めますか?
    • いいえ、各CTEは独立しています
    • はい、ただし自分より前に定義されたものだけ
    • はい、どんな順序でも
  3. 入れ子のサブクエリに対するCTEの主な利点は何ですか?
    • 常に速い
    • 永続的で再利用可能なテーブルを作る
    • 各ステップに名前を付け、流れを読みやすくテスト可能にする