> Как написать запрос с использованием 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, затем фильтрация по номеру строки.
Пример кода
SQLWITH ranked_orders AS (SELECTuser_id,order_id,amount,ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC) AS rnFROM ordersWHERE status = 'paid')SELECT user_id, order_id, amountFROM ranked_ordersWHERE rn = 1;
Пример в Go:
GOquery := `WITH ranked_orders AS (SELECT user_id, order_id, amount,ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC) AS rnFROM ordersWHERE status = $1)SELECT user_id, order_id, amountFROM ranked_ordersWHERE 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 - критическая уязвимость.
> Похожие задачи по backend
Приходилось ли работать с асинхронностью
В чем преимущество PostgreSQL перед MongoDB
Что такое protobuf и как он используется в gRPC
Что такое embedding
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью