вернуть пустые строки для несуществующих данных - PullRequest
2 голосов
/ 20 января 2020

Хорошо, у меня есть таблица со столбцом date и столбцом integer, и я хочу получить все строки, сгруппированные по дням даты в пределах определенного диапазона дат; поскольку не существует строк для каждого дня, можно ли заставить mysql возвращать строки для тех дней со значением по умолчанию?

пример

исходная таблица:

date         value
2020-01-01   1
2020-01-01   2
2020-01-03   2
2020-01-07   3
2020-01-08   4
2020-01-08   1

Стандартное поведение после group ввода по дате и sum значения ming:

2020-01-01   3
2020-01-03   2
2020-01-07   3
2020-01-08   5

Желаемое поведение / результат с пустыми строками:

2020-01-01   3
2020-01-02   0
2020-01-03   2
2020-01-04   0
2020-01-05   0
2020-01-06   0
2020-01-07   3
2020-01-08   5

Ответы [ 2 ]

2 голосов
/ 20 января 2020

Вы также можете достичь желаемого с помощью следующего запроса, который может быть проще для понимания:

SELECT
     date_table.date,
     IFNULL(SUM(value),0) as sum_val
FROM (
     SELECT DATE_ADD('2020-01-01', INTERVAL (@i:=@i+1)-1 DAY) AS `date`
     FROM information_schema.columns,(SELECT @i:=0) gen_sub
     WHERE DATE_ADD('2020-01-01',INTERVAL @i DAY) BETWEEN '2020-01-01' AND '2020-01-08'
) date_table
LEFT JOIN test ON test.date_value = date_table.date
GROUP BY date;

НАЙТИ ДЕМО ЗДЕСЬ

Вы можете установить некоторую переменную для фиксирования минимальной и максимальной дат:

SET @date_min = '2020-01-01';
SET @date_max = '2020-01-08';

SELECT DATE_ADD(@date_min, INTERVAL (@i:=@i+1)-1 DAY) AS `date`
FROM information_schema.columns, (SELECT @i:=0) gen_sub
WHERE DATE_ADD(@date_min, INTERVAL @i DAY) BETWEEN @date_min AND @date_max

Некоторое объяснение:

На самом деле, ваш вопрос побуждает нас генерировать набор даты, потому что мы ищем «левое соединение» с вашей таблицей с непрерывным набором дат, чтобы сопоставить даты без записей в «вашей таблице».

Это было бы довольно просто в PostgreSQL из-за функции generate_series, но это не так просто в MySQL, поскольку такой полезной функции не существует. Вот почему мы должны быть умными.

Оба решения имеют один и тот же лог c: я имею в виду, что они оба увеличивают значение даты (день в день) для каждой строки, объединенной в другой таблице, назовем ее «исходной таблицей». В ответе выше (не моем) «исходная таблица» состоит из множества объединений и перекрестных объединений (генерирует 100 тыс. Строк), в моем случае здесь «исходная таблица» - это «information_schema.columns», которая уже содержит много строк (1800 +).

В указанном выше случае начальная дата устанавливается на 1970-01-01, а затем она будет увеличивать эту дату в 100 000 раз, чтобы иметь набор из 100 000 дат, начинающихся с 1970-01-01.

В моем случае начальная дата фиксируется на минимальной дате диапазона 2020-01-01, а затем она будет увеличивать эту дату для каждой строки, найденной в information_schema.columns, так что примерно в 1800 раз. Вы закончите с набором из примерно 1800 дат, начиная с 2020-01-01.

Наконец, вы можете присоединиться к своей таблице с этим сгенерированным набором дат (каким бы способом это ни было), чтобы суммировать (значение) для каждого дня в нужном диапазоне.

Надеюсь, что это поможет вам понять логику c, стоящую за обоими запросами;)

2 голосов
/ 20 января 2020

Вы можете сделать что-то вроде следующего:

# table creation:

drop table if exists test_table;

create table test_table (your_date date, your_value int(11));
insert into test_table (your_date, your_value) values ('2020-01-01', 1);
insert into test_table (your_date, your_value) values ('2020-01-01', 2);
insert into test_table (your_date, your_value) values ('2020-01-03', 2);
insert into test_table (your_date, your_value) values ('2020-01-07', 3);
insert into test_table (your_date, your_value) values ('2020-01-08', 4);
insert into test_table (your_date, your_value) values ('2020-01-08', 1);

Это создаст список практически всех дат. Затем вы можете отфильтровать интересующие вас даты, объединить их со своей таблицей и группой.

Вы также можете заменить даты в операторе where подзапросами (минимальная и максимальная дата вашей таблицы), чтобы сделать ее динамической c

Это обходной путь, но он работает.

select sbqry.base_date, sum(ifnull(t.your_value, 0))
from (select adddate('1970-01-01',t4.i*10000 + t3.i*1000 + t2.i*100 + t1.i*10 + t0.i) base_date from
    (select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t0,
    (select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t1,
    (select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t2,
    (select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t3,
    (select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t4) sbqry
left join test_table t on base_date = t.your_date
where sbqry.base_date between '2020-01-01' and '2020-01-08'
group by sbqry.base_date;

ввод:

+------------+------------+
| your_date  | your_value |
+------------+------------+
| 2020-01-01 |          1 |
| 2020-01-01 |          2 |
| 2020-01-03 |          2 |
| 2020-01-07 |          3 |
| 2020-01-08 |          4 |
| 2020-01-08 |          1 |
+------------+------------+

вывод:

+------------+------------------------------+
| base_date  | sum(ifnull(t.your_value, 0)) |
+------------+------------------------------+
| 2020-01-01 |                            3 |
| 2020-01-02 |                            0 |
| 2020-01-03 |                            2 |
| 2020-01-04 |                            0 |
| 2020-01-05 |                            0 |
| 2020-01-06 |                            0 |
| 2020-01-07 |                            3 |
| 2020-01-08 |                            5 |
+------------+------------------------------+
...