> Как написать запрос, который выберет названия всех книг с более чем тремя авторами (Go)

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

Компании: Ozon

Стек: Go

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

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

Нужно сгруппировать записи по книге, посчитать количество авторов через COUNT и отфильтровать группы через HAVING. В зависимости от схемы - либо GROUP BY book_id HAVING COUNT(author_id) > 3, либо, если авторы хранятся в JSON-массиве, использовать json_array_length. В PostgreSQL с нормализованной схемой (таблицы books, authors, book_authors) запрос будет выглядеть как SELECT b.title FROM books b JOIN book_authors ba ON b.id = ba.book_id GROUP BY b.id, b.title HAVING COUNT(ba.author_id) > 3.

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

Ключевая идея - агрегация с фильтрацией групп. WHERE фильтрует строки до группировки, поэтому для условия "более трёх авторов" он не подходит - нужно сначала посчитать количество авторов на каждую книгу, а затем отфильтровать группы. Это делает HAVING.

Схема данных определяет синтаксис. При нормализованной схеме (many-to-many через junction table) группируем по book_id и считаем строки в book_authors. При денормализованной (JSON-массив в колонке authors) - используем json_array_length(authors) > 3 без группировки.

Важный нюанс: если в book_authors возможны дубликаты (например, одна и та же пара книга-автор записана дважды), нужно использовать COUNT(DISTINCT author_id). Также стоит помнить про NULL - если author_id может быть NULL, COUNT его не посчитает, что может исказить результат.

Для PostgreSQL с нормализованной схемой оптимальный индекс - (book_id, author_id) на junction table. Тогда группировка и подсчёт выполняются по индексу без обращения к таблице.

На практике

В реальном проекте с Go и PostgreSQL запрос обычно оборачивается в репозиторий. Типичная ошибка - пытаться использовать WHERE COUNT(*) > 3 вместо HAVING. Также часто забывают про DISTINCT, если модель данных допускает дубликаты.

Если книг с авторами очень много, стоит проверить план выполнения - без индекса на book_id запрос сделает seq scan. В некоторых СУБД (например, MySQL) HAVING можно использовать с алиасами, но в PostgreSQL лучше повторять выражение или использовать GROUP BY по всем выбранным колонкам.

Пример кода

SQL
-- Нормализованная схема: books, authors, book_authors
SELECT b.title
FROM books b
JOIN book_authors ba ON b.id = ba.book_id
GROUP BY b.id, b.title
HAVING COUNT(DISTINCT ba.author_id) > 3;
-- Денормализованная схема: колонка authors JSONB
SELECT title
FROM books
WHERE json_array_length(authors) > 3;

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

Начни с ключевой идеи: GROUP BY + HAVING. Объясни, почему WHERE не подходит - он фильтрует до агрегации. Уточни схему данных, потому что от неё зависит синтаксис. Покажи оба варианта - нормализованный и JSON. Упомяни про COUNT(DISTINCT ...) и индексы. Если спросят про производительность - скажи про индекс (book_id, author_id) и проверку через EXPLAIN.

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

Проверяется понимание разницы между WHERE и HAVING, умение работать с агрегатными функциями и группировкой. Также оценивается способность адаптировать запрос под схему данных и думать о производительности. Для senior-уровня важно упомянуть edge cases: дубликаты, NULL, индексы.

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

  • Использование WHERE COUNT(*) > 3 - синтаксическая ошибка, агрегаты нельзя использовать в WHERE.
  • Забывают GROUP BY по всем неагрегированным колонкам - в PostgreSQL это приведёт к ошибке, в MySQL может вернуть некорректные данные.
  • Не учитывают дубликаты в junction table - результат завышается.
  • При JSON-схеме пытаются использовать GROUP BY без необходимости.
  • Не проверяют план выполнения на больших таблицах.

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

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