WITHでクエリを名前の付いたステップに構造化し、変換のパイプラインのようにCTEを連鎖させましょう。
このレッスンを Kodokon で開く三段階の深さに入れ子になったサブクエリは、内側から外側へと読みます。読みにくく、半年後に見返すのも不可能です。CTE(Common Table Expression、共通テーブル式、WITH句)は読む順序を逆転させます。関数に名前を付けるのと同じように、各中間ステップに名前を付け、クエリは上から下へと読めます。このデータセットをsqliteonline.comまたはsqlite3に読み込みましょう。
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のテキストで、標準というよりエンジンの選択です)。
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;本当の力は連鎖から生まれます。一つのWITH、カンマで区切られた複数のCTE、それぞれが自分より前に定義されたものを読めます。パイプラインを組み立てるのです。集約し、統計を計算し、比較する。ここでは、売上が月次平均を超える月を探します(期待される結果:2026-03と2026-04、平均は260)。
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;