Условие
Составить отчёт «Нагрузка по часам»: показать количество заказов по каждому пункту заказа (ПЗ) в час, в процентном выражении.
- Строки: наименование ПЗ.
- Столбцы: часы (0–23).
- Значения: % от общего числа заказов в этом ПЗ.
- Срезы: направление, населённый пункт, район, признак ковид, год.месяц.
- Фильтры:
статус заказа NOT IN (6, 7),признак ПЗ = 'наши'.
Решение
Подход
Сначала считаем абсолютное количество по (ПЗ, час), потом делим на сумму по ПЗ — получаем долю. В BI это удобно через LOD (Tableau) или меру в DAX (Power BI). На SQL — через оконную функцию.
SQL
WITH base AS (
SELECT
pz_name,
EXTRACT(HOUR FROM order_dttm) AS hour,
COUNT(*) AS cnt
FROM orders o
WHERE o.status NOT IN (6, 7)
AND o.pz_flag = 'наши'
-- сюда уйдут срезы (направление, нас.пункт, район, ковид, год-месяц) как фильтры дашборда
GROUP BY pz_name, EXTRACT(HOUR FROM order_dttm)
),
totals AS (
SELECT pz_name, SUM(cnt) AS total
FROM base
GROUP BY pz_name
)
SELECT
b.pz_name,
b.hour,
100.0 * b.cnt / NULLIF(t.total, 0) AS pct_orders
FROM base b
JOIN totals t ON t.pz_name = b.pz_name
ORDER BY b.pz_name, b.hour;Tableau LOD
SUM([cnt]) / SUM({FIXED [pz_name] : SUM([cnt])})
Power BI / DAX
PctByPZ :=
DIVIDE(
COUNTROWS('orders'),
CALCULATE(COUNTROWS('orders'), ALLSELECTED('orders'[hour]))
)
Подводные камни
- Если строка
pz_nameимеет 0 заказов после фильтра — деление на ноль.NULLIFобязателен. - Срезы пользователь меняет в дашборде — формула должна реагировать. В Tableau
{FIXED [pz_name] : ...}правильно реагирует на фильтры контекста; в DAX —ALLSELECTEDвместоALL. - «Часы» — это локальный TZ или UTC? Если данные сырые — конвертировать.
- «Год.месяц» как срез — построить отдельное измерение
DATE_TRUNC('month', order_dttm). - Сумма по строке должна быть 100% (с округлением).
Эталонный ответ
Двухуровневая агрегация: подсчёт по (ПЗ, час) → деление на сумму по ПЗ. В BI — LOD/ALLSELECTED.