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 в одном запросе?
    • Ключевое слово WITH перед каждым CTE
    • Один WITH, а CTE разделены запятыми
    • Вложив их друг в друга
  2. Может ли CTE читать другой CTE из того же WITH?
    • Нет, каждый CTE изолирован
    • Да, но только те, что объявлены до него
    • Да, в любом порядке
  3. В чём главное преимущество CTE перед вложенным подзапросом?
    • Он всегда быстрее
    • Он создаёт постоянную переиспользуемую таблицу
    • Он именует каждый шаг и делает поток читаемым и проверяемым