我有一个接收短信的应用程序。我想要做的是使用mysql进行统计,这将在一小时内计算消息。例如,早上7点我收到了10条短信,早上8点我收到20条等等。我的表有这个列ID,smsText,smsDate ......(其他都不重要)。当我运行这个脚本时:
SELECT HOUR(smsDate), COUNT(ID) FROM SMS_MESSAGES GROUP BY HOUR(smsDate)
它显示我每小时收到多少消息。问题是当我没有收到任何消息,例如在下午5点,这个语句不会返回第17行计数0,我有这样的结果:
Hour Count
...
15 10
16 5
18 2
...
,我想要的是这个
Hour Count
...
15 10
16 5
17 0
18 2
...
我在网上搜索了一个解决方案,但是我不知道如何在我的网站上实现这个解决方案。希望有人可以帮助我。
答案 0 :(得分:3)
您可以创建一个包含所有小时数的表格并加入表格:
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
答案 1 :(得分:2)
由于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`
答案 2 :(得分:1)
观察: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)
这里发生了什么?三件事。
首先:DATE(smsDate) + INTERVAL HOUR(smsDate) HOUR
将任意时间戳转换为小时顶部的时间戳。这使我们可以获取表格中的第一个和最后一个小时时间戳。
其次,我们有一个子查询,它确定我们关心报告的第一个和最后一个小时(最小和最大smsDate)。
其次,我们有一个名为seq_0_to_999999的表。它包含一系列基数:从零开始的整数。更多关于此的内容。
将这两个表连接在一起,然后使用表达式
mintime + INTERVAL seq.seq HOUR AS msghour
我们可以获取一个连续运行每小时时间戳的表。
然后我们将其加入您的查询。在这里,它开始看起来更加复杂。我们正在大纲中这样做:
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
总而言之,它看起来像这样:
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
这将为您提供一个结果集,其中包含该范围内每小时的时间戳和计数。
最后,这个seq_0_to_999999
序列表怎么样?我们从零开始得到那些整数?答案是:我们必须安排这样做;这些数字不是内置于MySQL(MariaDB v10 +确实有它们)。
简单的方法是创建一个包含大量整数的表。但这会占用存储空间,所以我们会跳过它。
另一种方法是创建一个包含0-9整数的短表,如下所示:
DROP TABLE IF EXISTS seq_0_to_9;
CREATE TABLE seq_0_to_9 AS
SELECT 0 AS seq 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;
然后我们可以创建一个视图,将该表与自身连接起来,生成1000个这样的组合:
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
);
最后,我们可以将1000个数字表连接起来,创建一个视图,生成一百万个这样的组合:
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
);
这是一篇提供有关所有这些内容的更多信息的文章。 http://www.plumislandmedia.net/mysql/filling-missing-data-sequences-cardinal-integers/