SQLLab
Теория· 10 мин· 10 XP

Синтаксис WITH: пишем первый CTE

Базовый синтаксис

sql
WITH имя_cte AS (
    SELECT ...
    FROM ...
    WHERE ...
)
SELECT *
FROM имя_cte;

Ключевые правила:

  • WITH стоит перед SELECT
  • После AS идут скобки с подзапросом
  • Основной запрос идёт после закрывающей скобки — без WITH
  • После основного запроса нет точки с запятой внутри CTE

Пример: клиенты с активными счетами

Без CTE:

sql
SELECT c.first_name, c.last_name, a.account_type
FROM clients c
JOIN (SELECT id, client_id, account_type FROM accounts WHERE balance > 0) a
  ON a.client_id = c.id
ORDER BY c.last_name;

С CTE:

sql
WITH active_accounts AS (
    SELECT id, client_id, account_type
    FROM accounts
    WHERE balance > 0
)
SELECT c.first_name, c.last_name, a.account_type
FROM clients c
JOIN active_accounts a ON a.client_id = c.id
ORDER BY c.last_name;

Второй вариант читается как текст: «сначала берём активные счета, потом соединяем их с клиентами».

Несколько CTE через запятую

sql
WITH
cte1 AS (
    SELECT ...
),
cte2 AS (
    SELECT ...
    FROM cte1  -- можно ссылаться на предыдущий CTE!
)
SELECT *
FROM cte2;

Каждый следующий CTE может использовать предыдущий. Это позволяет строить пошаговые вычисления.

Пример: пошаговое вычисление

sql
WITH
-- Шаг 1: суммы по счетам
account_totals AS (
    SELECT account_id, SUM(amount) AS total
    FROM transactions
    WHERE txn_type = 'пополнение'
    GROUP BY account_id
),
-- Шаг 2: топ-счета (больше 20 000)
top_accounts AS (
    SELECT account_id, total
    FROM account_totals
    WHERE total > 20000
)
SELECT a.client_id, t.account_id, t.total
FROM top_accounts t
JOIN accounts a ON a.id = t.account_id
ORDER BY t.total DESC;

Что CTE не делает

CTE не создаёт реальную таблицу и не сохраняет данные. Это синтаксический сахар: база данных разворачивает CTE в тот же подзапрос перед выполнением (в большинстве случаев).

Частые ошибки

  • Запятая в конце последнего CTE — перед основным SELECT запятой нет:
    sql
    WITH cte AS (...),  -- ← лишняя запятая, ошибка!
    SELECT ...
    
  • Точка с запятой внутри CTE — запрос должен быть один, точка с запятой только в конце всего.

Запомни

WITH имя AS (подзапрос) → даём имя подзапросу. Несколько CTE разделяются запятой. Последний CTE — без запятой. Дальше идёт обычный SELECT, использующий CTE как таблицу.

Прочитайте урок до конца — прогресс засчитается автоматически