Коротко: CTE (Common Table Expression) — це іменований тимчасовий набір результатів, визначений у межах одного SQL-оператора через WITH … AS. Його головна цінність — структурованість і читабельність SQL-коду; вплив на швидкість залежить від конкретної СУБД, версії, плану виконання та кількості посилань на CTE. У 2026 році CTE залишається базовою навичкою для аналітиків, data engineers і розробників, які працюють із SQL.

Вступ

Новачки в SQL часто губляться, коли бачать конструкцію WITH у чужому коді. У більшості базових підручників її просто немає — там вчать SELECT, WHERE, JOIN, і на цьому зупиняються.

Тим часом CTE — один з найпоширеніших інструментів у реальних запитах аналітиків і data engineers. З появою dbt, хмарних DWH і low-code BI-платформ виникає закономірне питання: чи не застаріла ця техніка на фоні нових інструментів.

У цій статті розберемо базовий синтаксис CTE для тих, хто тільки починає, порівняємо CTE з підзапитами і подивимось, чи актуальний цей інструмент у 2026 році.

Що таке CTE і чому про нього досі сперечаються у 2026 році

Типова ситуація: новачок відкриває чужий SQL-запит для аналізу продажів і бачить блок WITH на самому початку. У підручнику про це не було, і перше враження — щось складне і зайве.

Насправді CTE — це спосіб дати ім’я результату підзапиту й використовувати його далі в межах того самого SQL-оператора як табличний вираз. CTE увійшли до стандарту SQL ще наприкінці 1990-х і сьогодні підтримуються практично всіма популярними реляційними та аналітичними СУБД.

Питання актуальності CTE виникає регулярно. Сучасні інструменти на кшталт dbt дозволяють будувати трансформації через модулі, хмарні DWH оптимізують запити автоматично, а low-code BI генерує SQL без участі людини. Логічно спитати: а чи потрібно взагалі розуміти, що таке CTE, якщо за тебе це робить інструмент.

Відповідь проста: розуміння CTE залишається базовою навичкою, тому що сучасні data-інструменти все одно активно використовують SQL, а CTE є одним зі стандартних способів структурувати складну логіку. Водночас важливо не сприймати CTE як окрему оптимізацію продуктивності — його поведінку визначає оптимізатор конкретної СУБД.

Що таке CTE в SQL простими словами: синтаксис для новачків

Базова конструкція WITH … AS

CTE будується за простою схемою: спочатку описуєш тимчасовий результат, потім використовуєш його в основному запиті.

WITH назва_cte AS (
    SELECT
        колонка1,
        колонка2
    FROM таблиця
    WHERE умова
)
SELECT *
FROM назва_cte
WHERE додаткова_умова;

Ключова відмінність від VIEW — CTE не створюється в каталозі бази як окремий постійний об’єкт. Його область видимості обмежена SQL-оператором, до якого належить WITH. Фізично результат може бути вбудований у загальний план, матеріалізований або повторно обчислений — це залежить від СУБД та оптимізатора.

Навіщо CTE, якщо є звичайний запит

Той самий результат можна отримати без CTE, через вкладений підзапит. Але коли підзапитів стає кілька, код перетворюється на “матрьошку”, яку важко читати і ще важче дебажити.

Ось приклад типової задачі: знайти клієнтів, які замовили товарів більш ніж на 10 000 грн.

WITH client_orders AS (
    SELECT
        client_id,
        SUM(order_amount) AS total_amount
    FROM orders
    GROUP BY client_id
)
SELECT
    client_id,
    total_amount
FROM client_orders
WHERE total_amount > 10000;

Тут одразу видно логіку кроків: спочатку агрегуємо суми по клієнтах, потім фільтруємо результат. Код читається зверху вниз, а не знизу вгору, як буває з вкладеними підзапитами.

Можна оголошувати кілька CTE в одному запиті через кому — це дозволяє розбити складну логіку на послідовні, зрозумілі етапи:

WITH client_orders AS (

    SELECT
        client_id,
        SUM(order_amount) AS total_amount
    FROM orders
    GROUP BY client_id

),

active_clients AS (

    SELECT client_id
    FROM client_orders
    WHERE total_amount > 10000

)

SELECT
    c.client_name,
    co.total_amount
FROM active_clients AS ac
INNER JOIN client_orders AS co
    ON ac.client_id = co.client_id
INNER JOIN clients AS c
    ON c.client_id = ac.client_id;

Для новачків це головна перевага CTE: не швидкість, а можливість структурувати мислення при написанні запиту.

CTE vs Subquery: у чому реальна різниця і коли що використовувати

Коли CTE виграє у читабельності

CTE і підзапит часто можуть описувати ту саму логіку, але відрізняються способом організації коду. CTE особливо зручний, коли потрібно розбити великий запит на іменовані етапи або звертатися до одного логічного блоку кілька разів.

Вкладений підзапит інколи доводиться дублювати, якщо той самий результат потрібен у кількох місцях. CTE усуває дублювання тексту SQL, але це не гарантує одноразового фізичного обчислення результату: різні СУБД можуть матеріалізувати CTE, вбудовувати його в основний план або повторно виконувати при кожному посиланні.

CTE чи subquery: що буде швидше

Продуктивність не визначається самим фактом використання WITH. У PostgreSQL 11 і старіших версіях SELECT-CTE зазвичай був optimization fence: результат обчислювався окремо, що могло завадити оптимізатору протиснути фільтри із зовнішнього запиту всередину CTE.

Починаючи з PostgreSQL 12, нерекурсивний side-effect-free CTE, на який посилаються один раз, за замовчуванням може бути вбудований у батьківський запит. Якщо посилань кілька, PostgreSQL зазвичай залишає окреме обчислення; поведінку можна явно керувати через MATERIALIZED і NOT MATERIALIZED. У SQL Server, навпаки, результати CTE не матеріалізуються автоматично: кожне зовнішнє посилання може вимагати повторного виконання визначення. У BigQuery CTE, використаний у кількох місцях, також може бути обчислений кілька разів.

Практичне правило: обирайте CTE або підзапит насамперед за читабельністю, а продуктивність перевіряйте планом виконання. Якщо дорогий проміжний результат використовується багато разів, інколи кращим рішенням буде тимчасова або матеріалізована таблиця. У PostgreSQL додатково варто знати MATERIALIZED / NOT MATERIALIZED; у SQL Server — пам’ятати, що CTE не є автоматичним кешем результату.

Рекурсивний CTE: коли без нього не обійтись

Як влаштований рекурсивний CTE

Рекурсивний CTE — стандартний і переносимий спосіб обходу ієрархічних або графоподібних даних у SQL без написання циклу в прикладному коді. Водночас це не єдиний механізм у всіх СУБД: наприклад, Oracle і Snowflake також мають CONNECT BY. Класичний рекурсивний CTE складається з якірної та рекурсивної частин, які зазвичай поєднуються через UNION ALL.

Перша частина — якірний запит, який визначає стартовий набір рядків. Друга — рекурсивний запит, що посилається на CTE і будує наступний рівень. Рекурсія завершується, коли чергова ітерація не повертає нових рядків; тому важливо, щоб умови join/filter і самі дані не створювали нескінченний цикл.

Практичний приклад: організаційна ієрархія

Класичний сценарій — побудова дерева підпорядкування співробітників, де кожен має посилання на свого керівника.

WITH RECURSIVE org_chart AS (

    -- якірна частина: топ-менеджери без керівника
    SELECT
        employee_id,
        employee_name,
        manager_id,
        1 AS level
    FROM employees
    WHERE manager_id IS NULL

    UNION ALL

    -- рекурсивна частина: додаємо підлеглих
    SELECT
        e.employee_id,
        e.employee_name,
        e.manager_id,
        oc.level + 1
    FROM employees AS e
    INNER JOIN org_chart AS oc
        ON e.manager_id = oc.employee_id

)

SELECT *
FROM org_chart
ORDER BY level, employee_name;

Без рекурсивного CTE цю задачу можна вирішувати й іншими способами — наприклад, CONNECT BY у СУБД, де він підтримується, процедурним кодом або логікою застосунку. Проте рекурсивний CTE залишається одним із найпоширеніших і найпереносиміших способів працювати з оргструктурами, деревами категорій і ланцюжками залежностей.

Синтаксис і обмеження рекурсивних CTE відрізняються між СУБД. PostgreSQL і MySQL 8+ використовують WITH RECURSIVE. У SQL Server слово RECURSIVE не пишеться; сервер також має MAXRECURSION (за замовчуванням 100 рівнів) для захисту від некоректної рекурсії. У MySQL глибину обмежує cte_max_recursion_depth (типово 1000). Snowflake підтримує рекурсивні CTE та окремо CONNECT BY. Перед продакшн-використанням варто звіряти синтаксис і ліміти з документацією конкретної платформи.

Типові помилки та обмеження CTE, про які мовчать туторіали

Помилки новачків при роботі з CTE

Перша типова помилка — спроба звернутись до CTE з іншого запиту. CTE існує тільки в межах того запиту, де його оголосили, на відміну від VIEW чи тимчасової таблиці, які зберігаються довше.

Друга поширена проблема — надмірне дроблення запиту на CTE «заради краси». П’ять або десять дрібних CTE не обов’язково роблять код простішим; іноді прямий JOIN або один добре названий підзапит читається краще. Вибір має виходити зі зрозумілості логіки, а не з бажання використати WITH всюди.

Третя проблема стосується рекурсивних CTE — некоректна логіка переходу між рівнями або циклічні дані можуть спричинити надмірну чи нескінченну рекурсію. Завершення рекурсії формально відбувається тоді, коли наступна ітерація не повертає рядків; на практиці корисно додавати обмеження глибини там, де це можливо, і тестувати запит на циклах та аномальних даних.

Обмеження, які варто знати перед продакшн-використанням

CTE не має власних індексів як постійна фізична таблиця. Для великих обсягів даних варто аналізувати execution plan і перевіряти, чи оптимізатор вбудовує CTE, матеріалізує його або повторно обчислює. Якщо проміжний результат дорогий і потрібен багато разів, тимчасова чи матеріалізована таблиця інколи буде кращою архітектурою.

EXPLAIN ANALYZE
WITH client_orders AS (
    SELECT
        client_id,
        SUM(order_amount) AS total_amount
    FROM orders
    GROUP BY client_id
)
SELECT *
FROM client_orders
WHERE total_amount > 10000;

У PostgreSQL цей EXPLAIN ANALYZE допоможе побачити фактичний план, час і те, чи з’являється окремий CTE Scan / materialization або CTE був вбудований у загальний план. В інших СУБД синтаксис аналізу плану відрізняється: наприклад, SQL Server використовує execution plan, а BigQuery — власні query plan / execution details.

Моя практична позиція: чи варто вчити CTE у 2026 році

CTE — це не модна фіча і не тренд, який з’явився нещодавно. Це базова навичка написання SQL-коду, яка не залежить від того, які інструменти зараз популярні.

Складність аналітичних задач робить CTE корисним способом розбивати запит на логічні етапи без переходу до процедурного коду. Але CTE — лише один із таких інструментів: іноді підзапит, VIEW, тимчасова таблиця або окрема dbt-модель буде доречнішою. У реальних проєктах цінність CTE найбільше відчувається в підтримуваності коду.

Водночас CTE не панацея. Для великих повторно використовуваних трансформацій краще винести логіку в dbt-модель, VIEW, materialized view або фізичну проміжну таблицю — залежно від вимог до повторного використання й продуктивності. CTE найкраще працює як локальний інструмент структурування логіки всередині одного оператора.

Той, хто не розуміє CTE, регулярно матиме труднощі з читанням сучасного аналітичного SQL. Вчити цю конструкцію варто не заради окремого пункту в резюме, а заради вміння декомпозувати складні запити й одночасно розуміти, що читабельність і фізичний план виконання — це різні речі.

FAQ: питання про CTE

Питання: Що таке CTE?
Відповідь: CTE (Common Table Expression) — це іменований тимчасовий набір результатів, визначений через WITH у межах одного SQL-оператора. Він допомагає розбити складну логіку на зрозумілі блоки. CTE не є постійною таблицею чи VIEW, а його фізичне виконання — inline, materialization або повторний розрахунок — залежить від СУБД та плану запиту.

Питання: Як почати використовувати CTE в SQL?
Відповідь: Достатньо написати конструкцію WITH ім’я_cte AS (SELECT …) перед основним запитом, а потім звертатись до цього імені як до звичайної таблиці. Це особливо зручно, коли потрібно виконати кілька послідовних обчислень або уникнути дублювання коду. Більшість сучасних СУБД, включно з PostgreSQL, MySQL та SQL Server, підтримують цей синтаксис без додаткових налаштувань.

Питання: CTE vs підзапити — що краще використовувати?
Відповідь: Універсально кращого варіанта немає. CTE часто виграє в читабельності та зручності декомпозиції складної логіки; підзапит може бути компактнішим для простої одноразової операції. За продуктивністю треба дивитися execution plan конкретної СУБД: одна система може вбудувати CTE у загальний план, інша — матеріалізувати або повторно виконати його.

Висновок

CTE залишається фундаментальним, а не застарілим інструментом SQL у 2026 році. Ключові речі, які варто запам’ятати: синтаксис WITH … AS, область видимості в межах одного оператора, відмінність від підзапиту на рівні структури коду, особливості рекурсивних CTE та те, що продуктивність визначає оптимізатор конкретної СУБД.

Вміння структурувати запити через CTE — одна з базових навичок, яка відрізняє впевненого дата-аналітика від того, хто просто копіює готові запити з інтернету. Спробуйте переписати кілька власних запитів із вкладеними підзапитами на CTE — різниця в читабельності стане помітною одразу.

Що далі: як прокачати SQL і CTE на практиці

Найкращий спосіб закріпити тему — практика на власних запитах. Візьміть 2-3 складні SQL-конструкції з підзапитами, які вже писали, і перепишіть їх через CTE. Порівняйте, наскільки легше стало читати логіку кроків.

Наступний логічний крок — розібратись із рекурсивним CTE на реальних ієрархічних даних: оргструктурі компанії, деревах категорій товарів чи залежностях у проєктних задачах. Це та тема, яка на практиці зустрічається частіше, ніж здається новачкам.

А якщо хочете системно прокачати SQL, CTE і суміжні теми — варто розглянути спеціалізацію Data Analytics & BI Engineer від Data Lab, що надасть структурований шлях від базового SQL до впевненої роботи з реальними даними в аналітиці та BI, з практикою на кейсах, а не тільки з теорією.