Короткий ответ
Нужно использовать оконную функцию с PARTITION BY category: SUM(revenue) OVER (PARTITION BY category) даёт сумму по всей категории в каждой строке товара, после чего легко посчитать долю revenue / category_total, не теряя детализацию по товарам.
Как это работает подробнее
PARTITION BY делит все строки на независимые группы (аналогично GROUP BY), но, в отличие от GROUP BY, оконная функция не сворачивает строки в одну — каждая исходная строка сохраняется, и к ней просто «пристёгивается» агрегат по её группе.
Пример кода
question-40.sql
SELECT category, product_name, revenue,
SUM(revenue) OVER (PARTITION BY category) AS category_total,
ROUND(
100.0 * revenue / NULLIF(SUM(revenue) OVER (PARTITION BY category), 0),
1
) AS pct_of_category
FROM product_revenue;Что использовать на практике
- это ключевое отличие оконных функций от обычной агрегации — можно одновременно видеть детальный уровень (товар) и контекст уровня группы (категория) в одной строке, что удобно для отчётов типа «вклад каждого товара в выручку категории». Сумма
pct_of_categoryвнутри одной категории должна быть примерно равна 100%. Проверка:SELECT category, SUM(pct_of_category) FROM (...) GROUP BY category— для каждой категории около 100.
Подводные камни
- пытаются решить эту задачу через
GROUP BY categoryс подзапросом иJOINобратно к товарам — это тоже работает, но требует двух проходов и соединения, тогда как оконная функция решает задачу за один проход и без явногоJOIN; забывают, что безPARTITION BYоконная функция посчитает сумму по всей таблице, а не по категории.