Я не знаю, возможно ли это, но я хотел бы оптимизировать написанный мной запрос, чтобы получить все группы запроса между двумя датами, заполненные 0 значениями для отсутствующей даты в интервале.
Я использую MySQL 5.7
У меня есть таблица calendar
, содержащая все часы года (8760 строк)
CREATE TABLE calendar (
date datetime PRIMARY KEY
);
У меня есть spents
таблица, содержащая дату, пользователя, категорию и потраченное
CREATE TABLE spents (
date datetime NOT NULL,
user varchar(24) NOT NULL,
category enum('food', 'hobbies', 'clothing', 'taxes') NOT NULL,
spent int(5) unsigned NOT NULL DEFAULT '0',
UNIQUE KEY hourly_composite (date, user, category)
);
Допустим, таблица spents
содержит следующие строки:
+---------------------+------+----------+-------+
| date | user | category | spent |
+---------------------+------+----------+-------+
| 2018-10-01 10:00:00 | bob | food | 10 |
| 2018-10-01 11:00:00 | bob | hobbies | 50 |
| 2018-10-01 11:00:00 | bob | clothing | 30 |
| 2018-10-01 11:00:00 | bob | taxes | 3 |
| 2018-10-01 12:00:00 | bob | food | 30 |
| 2018-10-01 15:00:00 | bob | clothing | 25 |
| 2018-10-01 16:00:00 | bob | hobbies | 5 |
+---------------------+------+----------+-------+
Я хочу, например, чтобыполучить сумму, потраченную между 10 и 18 часов 2018-10-01 для пользователя bob .
Окончательный результат должен выглядеть следующим образом:
+---------------------+------+------------------------+-------------+
| hour | user | categories | total_spent |
+---------------------+------+------------------------+-------------+
| 2018-10-01 10:00:00 | bob | food | 10 |
| 2018-10-01 11:00:00 | bob | clothing,hobbies,taxes | 83 |
| 2018-10-01 12:00:00 | bob | food | 30 |
| 2018-10-01 13:00:00 | bob | | 0 |
| 2018-10-01 14:00:00 | bob | | 0 |
| 2018-10-01 15:00:00 | bob | clothing | 25 |
| 2018-10-01 16:00:00 | bob | hobbies | 5 |
| 2018-10-01 17:00:00 | bob | | 0 |
| 2018-10-01 18:00:00 | bob | | 0 |
+---------------------+------+------------------------+-------------+
Таким образом, запрос выглядит следующим образом:
-- get the scalar product of unique group and hour
SELECT hour, user,
IFNULL(GROUP_CONCAT(DISTINCT IF(hour = DATE_FORMAT(spents.date, "%Y-%m-%d %T") AND spent > 0, category, NULL)), "") AS categories,
SUM(IF(hour = DATE_FORMAT(spents.date, "%Y-%m-%d %T"), IFNULL(spent, 0), 0)) AS total_spent
FROM spents
CROSS JOIN
(
-- get all hours in the time interval
SELECT DATE_FORMAT(date, "%Y-%m-%d %T") AS hour
FROM calendar
WHERE date BETWEEN "2018-10-01 10:00:00" AND "2018-10-01 18:59:59"
GROUP BY hour
) AS interval_units
WHERE date BETWEEN "2018-10-01 10:00:00" AND "2018-10-01 18:59:59"
GROUP BY user, hour
ORDER BY user, hour;
Этот запрос работает отлично, но я не уверен, что это более оптимизированный способсделайте это.
Конечно, это очень упрощенная версия таблицы spents
, представьте себе уникальный ключ с 8+ столбцами для каждого часа дней и таким количеством строк в таблице (несколько миллионов).
Причина, по которой я использую таблицу calendar
, заключается в возможности получить исчерпывающий список всех часов между двумя датами.
Я также могу группировать по годам, месяцам, дням и днямнедели и т.д ...
РЕДАКТИРОВАТЬ:
вот оператор EXPLAIN:
+----+-------------+------------+------------+-------+------------------+------------------+---------+------+------+----------+-----------------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+------------+------------+-------+------------------+------------------+---------+------+------+----------+-----------------------------------------------------------+
| 1 | PRIMARY | spents | NULL | range | hourly_composite | hourly_composite | 5 | NULL | 7 | 100.00 | Using where; Using temporary; Using filesort |
| 1 | PRIMARY | <derived2> | NULL | ALL | NULL | NULL | NULL | NULL | 9 | 100.00 | Using join buffer (Block Nested Loop) |
| 2 | DERIVED | calendar | NULL | range | PRIMARY | PRIMARY | 5 | NULL | 9 | 100.00 | Using where; Using index; Using temporary; Using filesort |
+----+-------------+------------+------------+-------+------------------+------------------+---------+------+------+----------+-----------------------------------------------------------+