> Как написать запрос с использованием CTE в SQL (Go)

Уровень: senior · Роль: backend · Категория: Технические вопросы

Компании: Нетология

Стек: Go

> Пример ответа

Короткий ответ

CTE (Common Table Expression) - это временный именованный набор результатов, который существует в рамках одного запроса. Синтаксис: WITH name AS (SELECT ...) SELECT .... CTE улучшает читаемость, позволяет переиспользовать подзапросы и поддерживает рекурсию. В Go вы обычно формируете такой SQL как строку и передаёте в database/sql через QueryContext или ExecContext. Основное преимущество - декомпозиция сложных запросов без создания временных таблиц.

Подробное объяснение

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

  • Область видимости - CTE виден только в рамках одного запроса, не сохраняется на диске.
  • Множественные CTE - разделяются запятыми: WITH a AS (...), b AS (...) SELECT ....
  • Рекурсивные CTE - используют WITH RECURSIVE для обхода деревьев или генерации последовательностей.
  • Производительность - в большинстве СУБД CTE не материализуется автоматически (кроме PostgreSQL 12+ с MATERIALIZED), поэтому повторное использование может означать повторное выполнение подзапроса.

Отличие от подзапроса в FROM: CTE можно ссылаться на самого себя (рекурсия) и на другие CTE, определённые ранее. Это упрощает чтение и отладку сложной логики.

На практике

В Go вы работаете с CTE как с обычным SQL-текстом. Важно:

  • Использовать параметризованные запросы ($1, $2 для PostgreSQL, ? для MySQL) - CTE не влияет на это.
  • Для динамических условий (например, фильтры) собирать SQL через strings.Builder, но всегда с плейсхолдерами.
  • Если CTE используется для пагинации или агрегации, проверяйте план выполнения - иногда оптимизатор лучше справляется с обычным JOIN или подзапросом.
  • Для рекурсивных CTE в PostgreSQL нужно указывать LIMIT внутри рекурсивной части, чтобы избежать бесконечного цикла.

Типичный сценарий: выборка с дедупликацией через ROW_NUMBER() внутри CTE, затем фильтрация по номеру строки.

Пример кода

SQL
WITH ranked_orders AS (
SELECT
user_id,
order_id,
amount,
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC) AS rn
FROM orders
WHERE status = 'paid'
)
SELECT user_id, order_id, amount
FROM ranked_orders
WHERE rn = 1;

Пример в Go:

GO
query := `
WITH ranked_orders AS (
SELECT user_id, order_id, amount,
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC) AS rn
FROM orders
WHERE status = $1
)
SELECT user_id, order_id, amount
FROM ranked_orders
WHERE rn = 1`
rows, err := db.QueryContext(ctx, query, "paid")

Как отвечать на собеседовании

Начните с определения и синтаксиса, затем переходите к практическим сценариям: иерархические данные, дедупликация, разбиение сложного запроса. Упомяните рекурсивные CTE и их ограничения. Если спросят про производительность - честно скажите, что CTE не всегда материализуется, и в некоторых случаях лучше использовать временную таблицу или подзапрос. Приведите пример из своего опыта, где CTE упростил поддержку кода. Не углубляйтесь в специфику конкретной СУБД, если не спрашивают - но будьте готовы упомянуть различия между PostgreSQL и MySQL.

Что проверяет интервьюер

  • Понимание области видимости и жизненного цикла CTE.
  • Умение объяснить разницу между CTE и подзапросом, временной таблицей.
  • Знание рекурсивных CTE и их применения.
  • Практические навыки: как передать параметры, как избежать SQL-инъекций.
  • Понимание плана выполнения и потенциальных проблем с производительностью.

Типичные ошибки

  • Путаница между CTE и временной таблицей - CTE не сохраняется и не имеет индексов.
  • Использование CTE для простых запросов, где подзапрос или JOIN читается лучше.
  • Забывание RECURSIVE при рекурсивном определении - синтаксическая ошибка.
  • Предположение, что CTE выполняется один раз и кэшируется - в большинстве СУБД это не так.
  • Игнорирование параметризации: встраивание значений напрямую в SQL-строку в Go - критическая уязвимость.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью