Mysql 使用GROUP BY时包括缺少的(零计数)行
我有一个接收短信的应用程序。我想做的是用mysql做一个统计,统计一小时内的邮件数。例如,早上7点我收到了10条短信,早上8点我收到了20条等等。我的表有以下列ID、smsText、smsDate。。。(其他并不重要)。运行此脚本时:Mysql 使用GROUP BY时包括缺少的(零计数)行,mysql,count,group-by,aggregate-functions,Mysql,Count,Group By,Aggregate Functions,我有一个接收短信的应用程序。我想做的是用mysql做一个统计,统计一小时内的邮件数。例如,早上7点我收到了10条短信,早上8点我收到了20条等等。我的表有以下列ID、smsText、smsDate。。。(其他并不重要)。运行此脚本时: SELECT HOUR(smsDate), COUNT(ID) FROM SMS_MESSAGES GROUP BY HOUR(smsDate) 它显示了我每小时收到多少信息。问题是,当我没有收到任何消息时,例如在下午5点,此语句不会返回计数为0的第17行,我的
SELECT HOUR(smsDate), COUNT(ID) FROM SMS_MESSAGES GROUP BY HOUR(smsDate)
它显示了我每小时收到多少信息。问题是,当我没有收到任何消息时,例如在下午5点,此语句不会返回计数为0的第17行,我的结果如下:
Hour Count
...
15 10
16 5
18 2
...
SELECT DATE(smsDate) + INTERVAL HOUR(smsDate) HOUR, COUNT(ID)
FROM SMS_MESSAGES
JOIN (
SELECT mintime + INTERVAL seq.seq HOUR AS msghour
FROM (
SELECT MIN(DATE(smsDate) + INTERVAL HOUR(smsDate) HOUR) AS mintime,
MAX(DATE(smsDate) + INTERVAL HOUR(smsDate) HOUR) AS maxtime
FROM SMS_MESSAGES
) AS minmax
JOIN seq_0_to_999999 AS seq ON seq.seq < TIMESTAMPDIFF(HOUR,mintime,maxtime)
) AS sq
ON DATE(smsDate) + INTERVAL HOUR(smsDate) HOUR = msghour
GROUP BY DATE(smsDate) + INTERVAL HOUR(smsDate) HOUR
ORDER BY DATE(smsDate) + INTERVAL HOUR(smsDate) HOUR
DROP VIEW IF EXISTS seq_0_to_999;
CREATE VIEW seq_0_to_999 AS (
SELECT (a.seq + 10 * (b.seq + 10 * c.seq)) AS seq
FROM seq_0_to_9 a
JOIN seq_0_to_9 b
JOIN seq_0_to_9 c
);
DROP VIEW IF EXISTS seq_0_to_999999;
CREATE VIEW seq_0_to_999999 AS (
SELECT (a.seq + (1000 * b.seq)) AS seq
FROM seq_0_to_999 a
JOIN seq_0_to_999 b
);
,我想要的是这个
Hour Count
...
15 10
16 5
17 0
18 2
...
我在网上搜索了一个解决方案,是关于UNION的,但我不知道如何在我的网站上实现它。希望有人能帮助我。您可以创建一个包含所有小时的表并加入这些表:
CREATE TABLE IF NOT EXISTS `hours` (
`hour` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `hours` (`hour`) VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9), (10), (11), (12), (13), (14), (15), (16), (17), (18), (19), (20), (21), (22), (23);
SELECT hours.hour, count( SMS_MESSAGES.ID )
FROM hours
LEFT JOIN SMS_MESSAGES ON ( hours.hour = HOUR( SMS_MESSAGES.smsDate ) )
GROUP BY 1
正如hellocode回答的那样,创建一个包含小时值的新表是一个好方法,下面是通过使用union实现这一点的另一种方法
select t.`hour`,count(s.ID) from (
select 0 as `hour`
union
select 1 as `hour`
union
select 2 as `hour`
union
.
.
.
select 23 as `hour`
) t
left join SMS_MESSAGES s on(t.`hour` = hour(s.smsDate))
group by t.`hour`
观察:HOUR()
仅从时间戳中提取小时。查询中可能需要日期和时间。这个答案提供了日期和时间
您需要一种方法来获取一个虚拟表,其中包含适当范围内的所有每小时时间戳。然后需要将该表连接到聚合查询
第一件事:这里有一个查询,它将获得范围内的时间戳
SELECT mintime + INTERVAL seq.seq HOUR AS msghour
FROM (
SELECT MIN(DATE(smsDate) + INTERVAL HOUR(smsDate) HOUR) AS mintime,
MAX(DATE(smsDate) + INTERVAL HOUR(smsDate) HOUR) AS maxtime
FROM SMS_MESSAGES
) AS minmax
JOIN seq_0_to_999999 AS seq ON seq.seq < TIMESTAMPDIFF(HOUR,mintime,maxtime)
我们可以获取具有连续运行的每小时时间戳的表
然后我们将其加入到您的查询中。这就是它开始看起来更复杂的地方。我们正在这样做,大致如下:
SELECT DATE(smsDate) + INTERVAL HOUR(smsDate) HOUR, COUNT(ID)
FROM SMS_MESSAGES
JOIN ( /*the query above wit the sequence of timestamps*/) AS sq
ON DATE(smsDate) + INTERVAL HOUR(smsDate) HOUR = msghour
GROUP BY DATE(smsDate) + INTERVAL HOUR(smsDate) HOUR
ORDER BY DATE(smsDate) + INTERVAL HOUR(smsDate) HOUR
综上所述,它看起来是这样的:
Hour Count
...
15 10
16 5
18 2
...
SELECT DATE(smsDate) + INTERVAL HOUR(smsDate) HOUR, COUNT(ID)
FROM SMS_MESSAGES
JOIN (
SELECT mintime + INTERVAL seq.seq HOUR AS msghour
FROM (
SELECT MIN(DATE(smsDate) + INTERVAL HOUR(smsDate) HOUR) AS mintime,
MAX(DATE(smsDate) + INTERVAL HOUR(smsDate) HOUR) AS maxtime
FROM SMS_MESSAGES
) AS minmax
JOIN seq_0_to_999999 AS seq ON seq.seq < TIMESTAMPDIFF(HOUR,mintime,maxtime)
) AS sq
ON DATE(smsDate) + INTERVAL HOUR(smsDate) HOUR = msghour
GROUP BY DATE(smsDate) + INTERVAL HOUR(smsDate) HOUR
ORDER BY DATE(smsDate) + INTERVAL HOUR(smsDate) HOUR
DROP VIEW IF EXISTS seq_0_to_999;
CREATE VIEW seq_0_to_999 AS (
SELECT (a.seq + 10 * (b.seq + 10 * c.seq)) AS seq
FROM seq_0_to_9 a
JOIN seq_0_to_9 b
JOIN seq_0_to_9 c
);
DROP VIEW IF EXISTS seq_0_to_999999;
CREATE VIEW seq_0_to_999999 AS (
SELECT (a.seq + (1000 * b.seq)) AS seq
FROM seq_0_to_999 a
JOIN seq_0_to_999 b
);
然后,我们可以创建一个视图,将该表与其自身连接起来,以生成1000个组合,如下所示:
Hour Count
...
15 10
16 5
18 2
...
SELECT DATE(smsDate) + INTERVAL HOUR(smsDate) HOUR, COUNT(ID)
FROM SMS_MESSAGES
JOIN (
SELECT mintime + INTERVAL seq.seq HOUR AS msghour
FROM (
SELECT MIN(DATE(smsDate) + INTERVAL HOUR(smsDate) HOUR) AS mintime,
MAX(DATE(smsDate) + INTERVAL HOUR(smsDate) HOUR) AS maxtime
FROM SMS_MESSAGES
) AS minmax
JOIN seq_0_to_999999 AS seq ON seq.seq < TIMESTAMPDIFF(HOUR,mintime,maxtime)
) AS sq
ON DATE(smsDate) + INTERVAL HOUR(smsDate) HOUR = msghour
GROUP BY DATE(smsDate) + INTERVAL HOUR(smsDate) HOUR
ORDER BY DATE(smsDate) + INTERVAL HOUR(smsDate) HOUR
DROP VIEW IF EXISTS seq_0_to_999;
CREATE VIEW seq_0_to_999 AS (
SELECT (a.seq + 10 * (b.seq + 10 * c.seq)) AS seq
FROM seq_0_to_9 a
JOIN seq_0_to_9 b
JOIN seq_0_to_9 c
);
DROP VIEW IF EXISTS seq_0_to_999999;
CREATE VIEW seq_0_to_999999 AS (
SELECT (a.seq + (1000 * b.seq)) AS seq
FROM seq_0_to_999 a
JOIN seq_0_to_999 b
);
最后,我们可以将包含1000个数字的表与它自身连接起来,创建一个视图,该视图将生成一百万个这样的组合:
Hour Count
...
15 10
16 5
18 2
...
SELECT DATE(smsDate) + INTERVAL HOUR(smsDate) HOUR, COUNT(ID)
FROM SMS_MESSAGES
JOIN (
SELECT mintime + INTERVAL seq.seq HOUR AS msghour
FROM (
SELECT MIN(DATE(smsDate) + INTERVAL HOUR(smsDate) HOUR) AS mintime,
MAX(DATE(smsDate) + INTERVAL HOUR(smsDate) HOUR) AS maxtime
FROM SMS_MESSAGES
) AS minmax
JOIN seq_0_to_999999 AS seq ON seq.seq < TIMESTAMPDIFF(HOUR,mintime,maxtime)
) AS sq
ON DATE(smsDate) + INTERVAL HOUR(smsDate) HOUR = msghour
GROUP BY DATE(smsDate) + INTERVAL HOUR(smsDate) HOUR
ORDER BY DATE(smsDate) + INTERVAL HOUR(smsDate) HOUR
DROP VIEW IF EXISTS seq_0_to_999;
CREATE VIEW seq_0_to_999 AS (
SELECT (a.seq + 10 * (b.seq + 10 * c.seq)) AS seq
FROM seq_0_to_9 a
JOIN seq_0_to_9 b
JOIN seq_0_to_9 c
);
DROP VIEW IF EXISTS seq_0_to_999999;
CREATE VIEW seq_0_to_999999 AS (
SELECT (a.seq + (1000 * b.seq)) AS seq
FROM seq_0_to_999 a
JOIN seq_0_to_999 b
);
这里有一封信提供了更多关于这一切的信息