Страницы

Поиск по вопросам

понедельник, 17 июня 2019 г.

Заполнить отсутствующие строки SQL?

Есть такой запрос:
SELECT DATE_FORMAT(`orders`.`date`, '%d.%m.%Y') as date, SUM((`orders_items`.`price`)*(`orders_items`.`count`)) AS `money` FROM `orders_items` LEFT JOIN `orders_groups` ON `orders_items`.`order_group_id` = `orders_groups`.`id` LEFT JOIN `orders` ON `orders_groups`.`order_id` = `orders`.`id` LEFT JOIN `goods` ON `goods`.`id` = `orders_items`.`good_id` LEFT JOIN `users` ON `orders`.`user_id` = `users`.`id` LEFT JOIN `orders_status` ON `orders_status`.`id` = `orders_items`.`status_id` WHERE date(`orders`.`date`) BETWEEN '2016-08-02' AND '2016-08-15' GROUP BY DATE_FORMAT(`orders`.`date`, '%d.%m.%Y') ORDER BY `orders`.`date` DESC
Результат получается такой:

Как можно заметить, между датами 11.08.2016 и 15.08.2016 не хватает 4 дня. То есть, в эти дни не было записей (0 строк). Собственно вопрос, как выводить все даты в указанном в BETWEEN промежутке, даже если строк нету (12.08.2016 – 0, 13.08.2016 – 0...)?


Ответ

mysql... Дополните данные на приложении, это будет проще. Нативных простых способов нет, вызов хранимки в from использовать нельзя, всё печально.
Если всё-таки хочется силами mysql - вам понадобится опорная таблица с датами, к которой уже left join вашу выборку. Можете нагенерировать предварительно таблицу из одного столбца типа date (тысячи лет вам хватит? Всего лишь неполные 366 тысячи записей, то есть мелочь), можете строить на приложении огромный
select '2016-08-11' as day union all select '2016-08-12' union all select '2016-08-13' /*и так далее*/
Можно воспользоваться какой-нибудь магией неочевидных декартовых произведений чисел и конвертацией в даты. Для небольших выборок (типичных для разбивки по дням, кстати. 1000 дней - это фигня для такого запроса, но почти 3 года календарного времени) даже может адекватно шевелиться. Что-то вроде такого:
SELECT :start + interval 5*5*(a-1)+5*(b-1) + c day AS d FROM ( SELECT 1 a UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 ) x CROSS JOIN ( SELECT 1 b UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 ) y CROSS JOIN ( SELECT 1 c UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 ) z WHERE :start + interval 5*5*(a-1)+5*(b-1) + c day < :stop ORDER BY d;
На стороне приложения опять же этот запрос надо генерировать с нужным числом cross join и правильным построением интервала.
PS: запрос, видимо, упрощён для вопроса (иначе стоит убрать пару лишних джойнов), но условие вида WHERE date(ordersdate) operator - прямой запрет на использование индекса по этому полю. А судя по запросу - это поле довольно селективно и индекс весьма пригодился бы. Используйте
where `orders`.`date` >= '2016-08-02' and `orders`.`date` <= '2016-08-15' + interval 1 day

Комментариев нет:

Отправить комментарий