Обновить

SQL для аналитика в ритейле: разбор 4 задач с данными (от простого к сложному)

Уровень сложностиПростой
Время на прочтение7 мин
Охват и читатели7.6K
Всего голосов 3: ↑3 и ↓0+3
Комментарии8

Комментарии 8

Задача 1. Топ-3 аптеки по выручке (базовый GROUP BY)

Я бы всё же использовал WITH TIES. И подогнал данные так, чтобы показать, что это даёт.

Пример вывода по данным: Парацетамол лидирует по количеству, так как это дешевый и ходовой товар, но по выручке обезболивающие дают больше.

Из полученных данных этого не видно без дополнительного расчёта в уме. Следовало бы применить WITH ROLLUP.

Хороший комментарий, действительно можно добавить подгонку, спасибо!

Пример вывода по данным: Даже пятая аптека дает >10% выручки – все точки важны, нет явных аутсайдеров.

А если есть аптеки, у которых вообще нет продаж, менеджер про это и не узнает, потому что использовал inner join.

Все так, но задача вывести «Топ-3 аптеки по выручке» - в этом случае и не нужно учитывать тех, у кого нет выручки

А как в топ-3 попала "даже пятая аптека"? Речь о том, что в "расширенной" версии запроса вполне могут быть аптеки, которые не продали ничего.

Ключевое, кажется, то, что выводим топ по выручке.

Хотя бы правы, может быть важным знаком полное ее отсутствие, если полное отсутствие выручки в целом вообще возможно - стоит его учесть.

Спасибо!

В примере с АВС анализом, для подсчета нарастающего итога вы используете row_number(), хотя можно было сразу его посчитать, добавив фрейм окна в виде ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW. Тогда в CTE второе выражение не потребовалось бы

WITH product_sales AS (

SELECT

pr.product_id,

pr.product_name,

pr.category,

SUM(s.amount) AS revenue,

SUM(SUM(s.amount)) OVER() AS total_revenue,

SUM(SUM(s.amount)) OVER(order BY SUM(s.amount) desc rows between unbounded preceding and current row) as cumulative_revenue

-- ROW_NUMBER() OVER(ORDER BY SUM(s.amount) DESC) AS revenue_rank

FROM sales s

JOIN products pr ON s.product_id = pr.product_id

GROUP BY pr.product_id, pr.product_name, pr.category

)

/*cumulative_calc AS (

SELECT

product_id,

product_name,

category,

revenue,

revenue_rank,

SUM(revenue) OVER(ORDER BY revenue_rank) AS cumulative_revenue,

total_revenue

FROM product_sales

)*/

SELECT

product_name,

category,

revenue,

ROUND(100.0 * revenue / total_revenue, 2) AS revenue_share,

ROUND(100.0 * cumulative_revenue / total_revenue, 2) AS cumulative_share,

CASE

WHEN cumulative_revenue <= 0.7 * total_revenue THEN 'A'

WHEN cumulative_revenue <= 0.9 * total_revenue THEN 'B'

ELSE 'C'

END AS abc_class

FROM product_sales

ORDER BY revenue desc

;

В "Задача 4. Кумулятивные продажи с детализацией (динамика во времени) простой вариант" вы наступаете на те же грабли, не обозначив фрейм для рассчета cumulative_revenue. На примере pharmacy_id =1 это хорошо видно. Так как в 2026-02-01 есть две продажи, а фрейм явно не указан (по умолчанию postgres берет range between). Поэтому для обеих продаж нет нарастания, а дана общая сумма.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации