Открыть сервис

Обобщённое табличное выражение

Обобщённое табличное выражение (англ. Common Table Expression, CTE) — это временный именованный набор результатов, который можно использовать в SQL-запросах в рамках одной инструкции SELECT, INSERT, UPDATE, DELETE или MERGE. CTE облегчает написание сложных запросов, делает их более читаемыми и поддерживаемыми, позволяя выносить подзапросы в отдельные логические блоки. В отличие от подзапроса, CTE может быть рекурсивным и ссылаться сам на себя, что особенно полезно для работы с иерархическими данными (например, деревьями, графами, оргструктурами).

История

Концепция обобщённых табличных выражений была введена в стандарте SQL:1999. Первыми СУБД, реализовавшими поддержку CTE, стали Oracle (начиная с версии 9.2, 2002 год) и PostgreSQL (начиная с версии 8.4, 2009 год). Впоследствии CTE были реализованы в Microsoft SQL Server (2005), IBM Db2, SQLite (3.8.3, 2014 год), MySQL (8.0, 2018 год) и других популярных реляционных системах управления базами данных.

До появления CTE разработчики были вынуждены использовать подзапросы, временные таблицы или представления для достижения той же функциональности. CTE предоставили более удобный и лаконичный инструмент, особенно для рекурсивных задач.

Описание и синтаксис

CTE задаётся с помощью ключевого слова WITH, за которым следует имя выражения и необязательный список столбцов. После ключевого слова AS в скобках указывается подзапрос (SELECT), формирующий набор данных. Основное тело запроса, использующее CTE, располагается после этого определения.

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

``sql WITH cte_name (column1, column2, ...) AS ( SELECT column1, column2, ... FROM some_table WHERE condition ) SELECT * FROM cte_name WHERE another_condition; ``

Преимущества использования CTE

Отличие от подзапросов

Подзапросы могут использоваться внутри WHERE, HAVING, FROM и других частей запроса, но они не могут быть переиспользованы в одном запросе без повторения. CTE, напротив, объявляется один раз и может многократно упоминаться в основном запросе и в других CTE (через цепочку). Кроме того, подзапросы нельзя ссылаться на себя (не рекурсивны), а CTE — можно.

Виды CTE

1. Не рекурсивные CTE

Простой временный набор данных, не ссылающийся сам на себя. Используется для изоляции промежуточных вычислений.

Пример. Поиск среднего количества продаж по категориям товаров и выделение категорий выше среднего:

``sql WITH category_sales AS ( SELECT category_id, AVG(amount) AS avg_amount FROM sales GROUP BY category_id ) SELECT c.category_name, cs.avg_amount FROM categories c JOIN category_sales cs ON c.id = cs.category_id WHERE cs.avg_amount > 100; ``

2. Рекурсивные CTE

Рекурсивное CTE — это выражение, которое ссылается само на себя в своём определении. Оно обязательно состоит из двух частей, объединённых ключевым словом UNION ALL (или UNION): базового (терминального) подзапроса и рекурсивного подзапроса.

Пример. Построение иерархии подчинённых для заданного руководителя:

``sql WITH RECURSIVE subordinates AS ( -- базовый подзапрос: выбираем начального руководителя SELECT id, name, manager_id, 1 AS level FROM employees WHERE id = 5 UNION ALL -- рекурсивный подзапрос: выбираем подчинённых SELECT e.id, e.name, e.manager_id, s.level + 1 FROM employees e JOIN subordinates s ON e.manager_id = s.id ) SELECT * FROM subordinates; ``

В данном примере subordinates последовательно расширяется, пока есть записи, ссылающиеся на уже найденные. Уровень (level) увеличивается с каждым шагом.

Ограничения рекурсии: большинство СУБД устанавливают максимальную глубину рекурсии (например, в PostgreSQL — 100 итераций по умолчанию, можно изменить с помощью SET max_recursive_iterations = 1000; в MySQL — ограничение cte_max_recursion_depth). Это предотвращает зацикливание.

Применение

CTE широко используются в следующих сценариях:

Поддержка в СУБД (краткий обзор)

СУБДВерсия с поддержкойОсобенности
Oracle9.2 (2002)Поддержка рекурсии с CONNECT BY и WITH RECURSIVE.
PostgreSQL8.4 (2009)Полноценная поддержка WITH RECURSIVE.
Microsoft SQL Server2005WITH; рекурсивные CTE поддерживаются.
SQLite3.8.3 (2014)WITH RECURSIVE поддерживается.
MySQL8.0 (2018)WITH RECURSIVE поддерживается.
Db29.7 (2009)WITH поддерживается, в том числе рекурсивные.
Firebird2.5 (2010)WITH RECURSIVE.

Интересные факты

Критика

Несмотря на удобство, у CTE есть недостатки:

Источники

BFOmetr — база данных и аналитика по компаниям России.

На главную BFOmetr →