Условие
Дана таблица T1(Fil, DO, Sales, Plan_pct) с продажами и выполнением плана по дополнительным офисам. Например:
| Fil | DO | Sales | Plan, % |
|---|---|---|---|
| Центральный | Москва 1 | 80 | 120% |
| Центральный | Москва 2 | 120 | 100% |
| Центральный | Тула | 30 | 80% |
| Дальневосточный | Хабаровск | 20 | 101% |
| Дальневосточный | Владивосток | 40 | 103% |
| Южный | Сочи | 60 | 95% |
| Южный | Анапа 2 | 80 | 105% |
| Северный | СПб 1 | 70 | 105% |
Нужно определить топ-3 филиала (Fil) по выполнению плана и вывести общую сумму продаж по каждому из них.
Решение
Подход
«Выполнение плана» по филиалу — обычно среднее или взвешенное среднее Plan, % по его офисам. Уточняем у заказчика, но безопаснее — взвешенное по Sales (большие офисы важнее).
WITH branch_perf AS (
SELECT Fil,
SUM(Sales) AS total_sales,
SUM(Sales * Plan_pct) / SUM(Sales) AS weighted_plan_pct,
AVG(Plan_pct) AS simple_avg_plan_pct
FROM T1
GROUP BY Fil
),
ranked AS (
SELECT Fil,
total_sales,
weighted_plan_pct,
RANK() OVER (ORDER BY weighted_plan_pct DESC) AS rk
FROM branch_perf
)
SELECT rk AS Place,
Fil,
total_sales AS Sales,
weighted_plan_pct AS plan_pct
FROM ranked
WHERE rk <= 3
ORDER BY rk;Альтернатива: по простому среднему
Если бизнес-логика «среднее без весов»:
SELECT Fil, SUM(Sales) AS Sales, AVG(Plan_pct) AS plan_pct
FROM T1
GROUP BY Fil
ORDER BY plan_pct DESC
LIMIT 3;Подводные камни
- Что значит «лучший филиал по выполнению плана» — взвешенное или простое среднее. Зависит от бизнес-смысла. Уточняйте.
RANKvsDENSE_RANKvsROW_NUMBER: при ничьих важно, что нужно.RANK— пропускает места (1, 2, 2, 4).DENSE_RANK— без пропусков (1, 2, 2, 3).ROW_NUMBER— всегда уникальные номера (1, 2, 3, 4).
Plan_pctкак строка'120%'— нужно конвертировать. В реальной БД хранится какnumeric(5,2).- Отрицательные продажи / возвраты: исключать или нет — бизнес-вопрос.
SUM(Sales * Plan_pct) / SUM(Sales)правильно считает weighted average, обращая plan_pct в численный.
Эталонный ответ
GROUP BY Fil → агрегация SUM(Sales) и weighted average Plan, % → RANK() OVER (ORDER BY plan_pct DESC) → отфильтровать rank <= 3.