Как написать запрос, который выберет названия всех книг с более чем тремя авторами
Что проверяют
Три вещи: умеете ли вы смоделировать связь «многие ко многим», знаете ли разницу между WHERE и HAVING и понимаете ли, как выполнить такой запрос из Go без утечки соединений. Следом часто просят вывести ещё и число авторов или показать книги без авторов.
Как отвечать
Сначала проговорите схему. У книги может быть несколько авторов, а у автора несколько книг, значит нужна связующая таблица: books(id, title), authors(id, name), book_authors(book_id, author_id) с первичным ключом по паре колонок. Колонка book_id в таблице авторов, как иногда предлагают, не даст одному автору написать две книги.
Запрос:
SELECT b.title FROM books b JOIN book_authors ba ON ba.book_id = b.id GROUP BY b.id, b.title HAVING COUNT(*) > 3;
Что стоит объяснить:
GROUP BY b.id: группировать по идентификатору, а не только по названию, потому что названия у разных книг могут совпадать;HAVING: фильтрует уже готовые группы, аWHEREотрабатывает до группировки, и агрегаты в нём недопустимы;- *`COUNT()
илиCOUNT(DISTINCT ...)**: первичный ключ(book_id, author_id)исключает дубли, и хватаетCOUNT(*). Если ключа нет, считайтеCOUNT(DISTINCT ba.author_id)`; - индекс: тот же первичный ключ начинается с
book_idи обслуживает группировку, а джойн кbooksидёт по её собственному первичному ключу.
В Go передайте порог параметром: db.QueryContext(ctx, q, 3) с $1 в тексте для PostgreSQL или ? для MySQL. Сразу после проверки ошибки defer rows.Close(), после цикла rows.Next() обязательно rows.Err(). В pgx v5 то же короче: pgx.CollectRows(rows, pgx.RowTo[string]).
Где спотыкаются
Пишут WHERE COUNT(a.id) > 3. База вернёт ошибку, агрегаты в WHERE не работают.
Группируют только по title. Две разные книги с одним названием склеятся в одну группу, и их авторы сложатся.
В Go забывают rows.Close() или проверку rows.Err(). Соединение не возвращается в пул, а ошибка, прервавшая чтение на середине, теряется, и функция отдаёт неполный список как успешный.