Перейти до основного вмісту
Модуль 7 · Урок 1 із 425/57 у курсі~12 min
Модульні уроки (1/4)

CTE: багатокрокові запити з WITH

Реальні запити мають тенденцію до зростання: три JOIN, GROUP BY, підзапит, інший підзапит… і вони стають нечитабельними. CTEs ("Загальна таблиця Вирази", речення WITH) дозволяє дати назву проміжний крок і використовуйте його як таблицю в основному запиті.

Основний синтаксис

SQL
WITH nome_cte AS (
  SELECT …    -- query interna
)
SELECT …      -- query principale, usa nome_cte come fosse una tabella
FROM   nome_cte;

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

приклад

Знайдіть 3 клієнтів, які витратили найбільше (сума над доставленими замовленнями):

SQL
WITH total_spent AS (
  SELECT o.customer_id,
         SUM(oi.quantity * oi.unit_price) AS total
  FROM   orders      o
  JOIN   order_items oi ON oi.order_id = o.id
  WHERE  o.status = 'delivered'
  GROUP BY o.customer_id
)
SELECT c.email,
       s.total
FROM   total_spent s
JOIN   customers c ON c.id = s.customer_id
ORDER BY s.total DESC
LIMIT  3;

Читайте англійською: "спочатку обчисліть витрати на клієнта; потім приєднайтеся клієнтів і візьміть перші 3". Без CTE вам потрібен вбудований підзапит у FROM (законно, але шумніше).

Кілька КТР у каскаді

Ви можете визначити кілька CTE, розділених комами, і кожен з них може посилатися до попередніх:

SQL
WITH total_spent AS (
  SELECT customer_id, SUM(quantity * unit_price) AS total
  FROM   order_items oi
  JOIN   orders      o ON oi.order_id = o.id
  WHERE  o.status = 'delivered'
  GROUP BY customer_id
),
average AS (
  SELECT AVG(total) AS total_average
  FROM   total_spent
)
SELECT c.email, s.total
FROM   total_spent s
JOIN   customers c ON c.id = s.customer_id
CROSS JOIN average
WHERE  s.total > average.total_average
ORDER BY s.total DESC;

CROSS JOIN average тому що average завжди має один рядок: Декартовий добуток просто додає total_average до кожного рядка результату.

CTE проти підзапиту: коли використовувати який

CTE сяють, коли:

  • той самий підзапит використовується два або більше разів в основному запиті;
  • ви хочете зробити структуру явною в іменованих кроках (самодокументування);
  • вони є передумовою для рекурсивних CTE (WITH RECURSIVE, наступний урок у цьому модулі).

Вбудований підзапит більш стислий для кроку, який використовується лише один раз.

Спробуйте

вправи#sql.m7.l1.e1
Спроби: 0Завантаження…

Використовуйте CTE під назвою «total_spent», який обчислює для кожного клієнта загальні витрати (SUM(quantity * unit_price)) на доставлені замовлення. Потім у головному запиті покажіть електронну адресу та загальну суму для 3 найбільших витрат. Упорядкувати за зменшенням загальної кількості, обмежити до 3.

Завантаження редактора…
Показати підказку

Всередині CTE сума кількості * unit_price. У головному запиті JOIN клієнтів на id.

Рішення доступне після 3 спроб

Повторна вправа

вправи#sql.m7.l1.e2
Спроби: 0Завантаження…

Той самий CTE 'total_spent', що й у попередній вправі. У головному запиті відобразіть електронну адресу та загальну суму для клієнтів, чиї витрати перевищують середні витрати (для всіх клієнтів із принаймні одним доставленим замовленням). Порядок за зменшенням загальної кількості.

Завантаження редактора…
Показати підказку

Скалярний підзапит у WHERE: SELECT AVG(total) FROM total_spent.

Рішення доступне після 3 спроб