Короткий ответ
Нужно сгенерировать полный список дат (через generate_series в PostgreSQL или аналог в другом диалекте) и сделать LEFT JOIN к агрегированным по дням продажам, заменив NULL в сумме на 0 через COALESCE.
Как это работает подробнее
Как это работает: generate_series(start_date, end_date, interval '1 day') в PostgreSQL возвращает по одной строке на каждый день периода; LEFT JOIN сохраняет все эти даты независимо от того, нашлась ли для них продажа, и COALESCE(SUM(amount), 0) явно превращает отсутствие продаж в 0, а не в пропущенную точку графика.
Пример кода
PostgreSQL: полный календарный ряд
WITH days AS (
SELECT day::date
FROM generate_series(DATE '2024-01-01', DATE '2024-01-31', INTERVAL '1 day') AS g(day)
), daily_sales AS (
SELECT order_date::date AS day, SUM(amount) AS amount
FROM orders
WHERE order_date >= DATE '2024-01-01'
AND order_date < DATE '2024-02-01'
GROUP BY order_date::date
)
SELECT d.day, COALESCE(s.amount, 0) AS daily_amount
FROM days AS d
LEFT JOIN daily_sales AS s USING (day)
ORDER BY d.day;Что использовать на практике
- На практике: без этого шага график временного ряда просто «перескочит» через дни без продаж, что может визуально искажать тренд (линия на графике соединит соседние по данным, но не по времени точки) и затруднить расчёт корректных средних за период, включающих нулевые дни.
- Проверка: Результат — ровно 31 строка, включая дни с
daily_amount = 0. Проверка:SELECT COUNT(*)должен быть 31 независимо от того, были ли продажи каждый день.
Подводные камни
- Частые ошибки: строят график прямо по результату
GROUP BY order_dateбез досоздания пропущенных дат, из-за чего дни без продаж просто отсутствуют на графике вместо явного нуля — это особенно критично для низкочастотных событий (например, продажи редкого товара).