MySQL: выберите все даты в диапазоне, даже если записей нет
У меня есть база данных пользователей. Я хотел бы создать график на основе роста пользовательской базы. Теперь у меня есть запрос:
SELECT DATE(datecreated), count(*) AS number FROM users
WHERE DATE(datecreated) > '2009-06-21' AND DATE(datecreated) <= DATE(NOW())
GROUP BY DATE(datecreated) ORDER BY datecreated ASC
Это возвращает почти то, что я хочу. Если однажды мы получим 0 пользователей, этот день не будет возвращен как значение 0, он просто пропущен, а на следующий день будет возвращен хотя бы один пользователь. Как я могу получить что-то вроде (psuedo-response):
date1 5
date2 8
date3 0
date4 0
date5 9
etc...
где даты с нулем отображаются в последовательном порядке с остальными датами?
Спасибо!
Ответы
Ответ 1
Надеюсь, вы поймете остальное.
select * from (
select date_add('2003-01-01 00:00:00.000', INTERVAL n5.num*10000+n4.num*1000+n3.num*100+n2.num*10+n1.num DAY ) as date from
(select 0 as num
union all select 1
union all select 2
union all select 3
union all select 4
union all select 5
union all select 6
union all select 7
union all select 8
union all select 9) n1,
(select 0 as num
union all select 1
union all select 2
union all select 3
union all select 4
union all select 5
union all select 6
union all select 7
union all select 8
union all select 9) n2,
(select 0 as num
union all select 1
union all select 2
union all select 3
union all select 4
union all select 5
union all select 6
union all select 7
union all select 8
union all select 9) n3,
(select 0 as num
union all select 1
union all select 2
union all select 3
union all select 4
union all select 5
union all select 6
union all select 7
union all select 8
union all select 9) n4,
(select 0 as num
union all select 1
union all select 2
union all select 3
union all select 4
union all select 5
union all select 6
union all select 7
union all select 8
union all select 9) n5
) a
where date >'2011-01-02 00:00:00.000' and date < NOW()
order by date
С
select n3.num*100+n2.num*10+n1.num as date
вы получите столбец с номерами от 0 до max (n3) * 100 + max (n2) * 10 + max (n1)
Так как здесь max n3 равно 3, SELECT вернет 399 плюс 0 → 400 записей (даты в календаре).
Вы можете настроить свой динамический календарь, ограничив его, например, с min (date), который вы должны сейчас().
Ответ 2
Этот вопрос спрашивает то же самое, что я думаю. Как правило, принятый ответ, похоже, заключается в том, что вы либо делаете это в своей логике приложения (читайте в том, что у вас есть в массиве, затем перебираете массив и создаете отсутствующие даты), либо используете временные таблицы, заполненные датами, которые вы хотите присоединиться.
Ответ 3
Это лучше сделать так:
-- 7 Days:
set @n:=date(now() + interval 1 day);
SELECT qb.day_series as days , COALESCE(col_byte, 0) as Bytes from tbl1 qa
right join (
select (select @n:= @n - interval 1 day) day_series from tbl1 limit 7 ) as qb
on date(qa.Timestamp) = qb.day_series and
qa.Timestamp > DATE_SUB(curdate(), INTERVAL 7 day) order by qb.day_series asc
-- 30 Days:
set @n:=date(now() + interval 1 day);
SELECT qb.day_series as days , COALESCE(col_byte, 0) as Bytes from tbl1 qa
right join (
select (select @n:= @n - interval 1 day) day_series from tbl1 limit 30 ) as qb
on date(qa.Timestamp) = qb.day_series and
qa.Timestamp > DATE_SUB(curdate(), INTERVAL 30 day) order by qb.day_series asc;
или без переменной:
SELECT qb.day_series as days , COALESCE(col_byte, 0) as Bytes from tbl1 qa
right join (
select curdate() - INTERVAL a.a day as day_series from(
select 0 as a union all select 1 union all select 2 union all
select 3 union all select 4 union all
select 5 union all select 6 union all select 7
) as a ) as qb
on date(qa.Timestamp) = qb.day_series and
qa.Timestamp > DATE_SUB(curdate(), INTERVAL 7 day) order by qb.day_series asc;
Ответ 4
Сделайте правильное внешнее соединение с таблицей, назовите его tblCalendar, который предварительно заполнен датами, о которых вы хотите сообщить. И присоединитесь к полю даты.
Пол
Ответ 5
При дальнейшей мысли, что-то вроде этого должно быть то, что вы хотите:
CREATE TEMPORARY TABLE DateSummary1 ( datenew timestamp ) SELECT DISTINCT(DATE(datecreated)) as datenew FROM users;
CREATE TEMPORARY TABLE DateSummary2 ( datenew timestamp, number int ) SELECT DATE(datecreated) as datenew, count(*) AS number FROM users
WHERE DATE(datecreated) > '2009-06-21' AND DATE(datecreated) <= DATE(NOW())
GROUP BY DATE(datecreated) ORDER BY datecreated ASC;
SELECT ds1.datenew,ds2.number FROM DateSummary1 ds1 LEFT JOIN DateSummary2 ds2 on ds1.datenew=ds2.datenew;
Это дает вам все даты в первой таблице и сводные данные count
во второй таблице. Возможно, вам потребуется заменить ds2.number
на IF(ISNULL(ds2.number),0,ds2.number)
или что-то подобное.
Ответ 6
Запрос:
SELECT qb.dy as yourday, COALESCE(count(yourcolumn), 0) as yourcount from yourtable qa
right join (
select curdate() as dy union
select DATE_SUB(curdate(), INTERVAL 1 day) as dy union
select DATE_SUB(curdate(), INTERVAL 2 day) as dy union
select DATE_SUB(curdate(), INTERVAL 3 day) as dy union
select DATE_SUB(curdate(), INTERVAL 4 day) as dy union
select DATE_SUB(curdate(), INTERVAL 5 day) as dy union
select DATE_SUB(curdate(), INTERVAL 6 day) as dy
) as qb
on qa.dates = qb.dy
and qa.dates > DATE_SUB(curdate(), INTERVAL 7 day)
order by qb.dy asc;
и результат:
+------------+-----------+
| yourday | yourcount |
+------------+-----------+
| 2015-06-24 | 274339 |
| 2015-06-25 | 0 |
| 2015-06-26 | 0 |
| 2015-06-27 | 0 |
| 2015-06-28 | 134703 |
| 2015-06-29 | 87613 |
| 2015-06-30 | 0 |
+------------+-----------+