Условие
Дан софт, автоматизирующий работу библиотеки. 3НФ-схема такая:
Произведение(id, название)Издание(id, id_произведения, год_издания, кол-во_страниц)Экземпляр(id, id_издание, инвентарный_номер)Лог_операций(id, id_user, id_экземпляр, дата_взяли, дата_вернули)
Найти произведения, которые издавались более 5 раз. Решение должно быть оптимальным: если можно без вложенного запроса/CTE — нужно без них.
Решение
Идея
«Издаваемость» — это количество строк в таблице Издание для данного произведения. Достаточно одной агрегации GROUP BY id_произведения + фильтра HAVING COUNT(*) > 5.
JOIN c таблицей Произведение нужен только если хотим вывести название (без него возвращаем id_произведения).
Код
Минимальный вариант (только id):
SELECT id_произведения,
COUNT(*) AS editions_count
FROM Издание
GROUP BY id_произведения
HAVING COUNT(*) > 5;С названием:
SELECT p.id,
p.название,
COUNT(i.id) AS editions_count
FROM Произведение p
JOIN Издание i ON i.id_произведения = p.id
GROUP BY p.id, p.название
HAVING COUNT(i.id) > 5
ORDER BY editions_count DESC;Сложность / проверка
- Один проход по
Издание+ хеш-агрегация. На больших данных — O(N). - Если есть индекс на
id_произведения— план будет «Index Scan + GroupAggregate».
Подводные камни
WHERE COUNT(*) > 5— частая ошибка.COUNTагрегатная и фильтруется черезHAVING, неWHERE.- Дублирование строк при JOIN. Если по неосторожности приджойнить
ЭкземплярилиЛог_операций—COUNTпосчитает уже не издания, а экземпляры/выдачи. Считаем только по таблицеИздание. > 5vs>= 5. Условие говорит «более 5» — это строго> 5, то есть от 6 включительно.- Произведения без изданий. Они не попадут в результат — это корректно: «не издавалось» != «издавалось ≤5 раз».
- Подзапрос вместо
HAVING. В стандартах ANSI можно черезWITH+ фильтр, но это лишняя работа: задание прямо указывает, что вложенные запросы здесь не оптимальны.
Эталонный ответ
SELECT id_произведения, COUNT(*) FROM Издание GROUP BY id_произведения HAVING COUNT(*) > 5. Если нужно название — JOIN Произведение.