CTE SQL: WITH и рекурсивные запросы
CTE SQL позволяет вынести промежуточный результат в именованный блок WITH и использовать его дальше в том же запросе. Разберём несколько CTE подряд, передачу результата между ними и отличие обычного CTE от рекурсивного.
Обновлено · Команда SQL Arena
Что потренируете
- Разбивать запрос на несколько CTE
- Передавать результат одного CTE в следующий
- Заменять глубокие вложенные подзапросы на читаемые шаги
- Писать рекурсивные CTE для иерархий
- Строить последовательности дат и чисел
Пример задачи в тренажёре
Так выглядит задача в SQL Arena: условие, редактор, результат и схема базы.
В employees хранятся сотрудники и их зарплаты, а в teams — команды. Сначала посчитайте суммарный фонд зарплат каждой команды, затем отдельным CTE оставьте команды с фондом не меньше 200000. Верните team_name и payroll, отсортировав результат по фонду зарплат по убыванию.
WITH team_payroll AS ( SELECT team_id, SUM(salary) AS payroll FROM employees GROUP BY team_id ), ranked AS ( SELECT team_id, payroll FROM team_payroll WHERE payroll >= 200000 ) SELECT t.team_name, r.payroll FROM ranked r JOIN teams t ON t.id = r.team_id ORDER BY r.payroll DESC, t.team_name;
| # | team_name | payroll |
|---|---|---|
| 1 | Delta | 370000 |
| 2 | Atlas | 295000 |
| 3 | Cipher | 225000 |
В employees хранятся сотрудники и их зарплаты, а в teams — команды. Сначала посчитайте суммарный фонд зарплат каждой команды, затем отдельным CTE оставьте команды с фондом не меньше 200000. Верните team_name и payroll, отсортировав результат по фонду зарплат по убыванию.
WITH team_payroll AS ( SELECT team_id, SUM(salary) AS payroll FROM employees GROUP BY team_id ), ranked AS ( SELECT team_id, payroll FROM team_payroll WHERE payroll >= 200000 ) SELECT t.team_name, r.payroll FROM ranked r JOIN teams t ON t.id = r.team_id ORDER BY r.payroll DESC, t.team_name;
Как работает CTE SQL с конструкцией WITH
Первый CTE, team_payroll, решает одну конкретную задачу: группирует сотрудников по team_id и считает SUM(salary). На этом шаге нас ещё не интересуют названия команд. Результат — небольшая промежуточная таблица из идентификатора команды и суммы зарплат.
Второй CTE, ranked, читает уже не исходную таблицу, а результат team_payroll. Здесь остаются только команды с payroll >= 200000. Именно в этом удобство нескольких CTE подряд: каждый следующий шаг может работать с результатом предыдущего, поэтому запрос читается сверху вниз как последовательность преобразований.
Финальный SELECT присоединяет teams, чтобы заменить технический team_id на название команды. ORDER BY r.payroll DESC, t.team_name задаёт стабильный порядок. Тот же расчёт можно было спрятать во вложенный подзапрос, но при добавлении ещё двух-трёх этапов WITH обычно проще читать, проверять и менять.
WITH team_payroll AS ( SELECT team_id, SUM(salary) AS payroll FROM employees GROUP BY team_id ), ranked AS ( SELECT team_id, payroll FROM team_payroll WHERE payroll >= 200000 ) SELECT t.team_name, r.payroll FROM ranked r JOIN teams t ON t.id = r.team_id ORDER BY r.payroll DESC, t.team_name;
| team_name | payroll |
|---|---|
| Delta | 370000 |
| Atlas | 295000 |
| Cipher | 225000 |
Типичные ошибки при использовании CTE SQL
CTE написан, но не используется
WITH только объявляет промежуточный результат. Если финальный SELECT не читает этот CTE напрямую или через другой CTE, вычисление не становится частью ответа. Частая ошибка — создать правильный шаг, а затем случайно снова обратиться к исходной таблице.
Фильтрация выполняется не на том этапе
Если условие зависит от агрегата, например SUM(salary), применять его к отдельным строкам до группировки нельзя. Сначала нужно получить сумму по группе, а затем фильтровать готовый результат через следующий CTE или HAVING.
В рекурсивном CTE нет корректного условия остановки
Рекурсивный CTE состоит из начальной части и рекурсивного шага. Если следующий уровень снова и снова создаёт строки без ограничения, запрос продолжает расширять результат. Для иерархий важно правильно связать родителя и потомка, а для последовательностей — явно ограничить конечную дату или значение.
Задачи по теме в тренажёре
Трек «CTE: запрос из нескольких шагов»В SQL Arena есть отдельные CTE SQL задачи разной сложности. На простых примерах можно потренировать один WITH, например расчёт операционного баланса необанка или путь от заявки KYC до одобрения. Дальше появляются запросы из нескольких этапов: агрегации по категориям, анализ последовательности событий и расчёты по позициям строк. Для системной практики есть трек «CTE: запрос из нескольких шагов». В нём удобно отрабатывать не синтаксис WITH сам по себе, а построение запроса как цепочки небольших преобразований. Всего в тренажёре 1 192 задачи, поэтому таблицы и данные между задачами меняются. Решение проверяется на скрытых данных, а не только на строках, которые видны в условии.
- EasyбесплатноОперационный баланс необанкаpostgresql
- EasyбесплатноОт заявки до одобрения KYCpostgresql
- MediumPremiumЗаказы по категориямpostgresql
- MediumPremiumКрупные GET-запросы экспортаpostgresql
- MediumPremiumСредняя пауза между первым и вторым постомpostgresqlmysql
- MediumPremiumСуммы показаний на чётных и нечётных позицияхpostgresqlmysql
Справочник по теме
Подробные статьи с синтаксисом и примерами в справочнике SQL Arena.
Частые вопросы
Что такое CTE SQL?
CTE, или common table expression, — это именованный результат подзапроса, объявленный через WITH. На него можно ссылаться дальше в основном запросе или в следующих CTE того же выражения.
Как использовать CTE SQL?
После WITH указывают имя CTE и запрос в круглых скобках, а затем обращаются к этому имени как к источнику данных. Несколько CTE перечисляются последовательно через запятую.
Чем CTE SQL отличается от подзапроса?
Оба способа позволяют выделить промежуточное вычисление, но CTE даёт ему имя и располагает его перед основным запросом. Это может сделать многошаговый SQL легче для чтения и повторного использования внутри одного выражения.
Можно ли использовать несколько CTE в SQL?
Да. После WITH можно объявить несколько CTE, разделив их запятыми. Следующий CTE может обращаться к предыдущему, если это допускает конкретная структура запроса.
Что такое рекурсивный CTE SQL?
Рекурсивный CTE ссылается на собственный результат и повторяет вычисление до тех пор, пока рекурсивная часть перестаёт возвращать новые строки по заданной логике. Такой подход используют, например, для иерархий и последовательностей.
Практика CTE SQL на задачах
Откройте SQL Arena и попробуйте сначала задачи с одним WITH, а затем переходите к нескольким CTE и рекурсивным запросам. Если нужен последовательный маршрут, продолжите в треке «CTE: запрос из нескольких шагов».
Решать задачи на CTE