[英]including missing (zero-count) rows when using GROUP BY
我有一個接收短信的應用程序。 我想要做的是使用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
...
我在網上搜索了一個解決方案,但是我不知道如何在我的網站中實現這個解決方案。 希望可以有人幫幫我。
您可以創建一個包含所有小時數的表並加入表:
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)
這里發生了什么? 三件事。
第一個: 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/
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.