2014-06-19 25 views
3

我有一個接收短信的應用程序。我想要做的是使用mysql統計一個小時內的統計信息。例如,在上午7點,我收到了10個短信,上午8點我收到了20個等。我的表有這個列ID,smsText,smsDate ...(其他不重要)。當我運行這個腳本:包括使用GROUP BY時丟失(零計數)的行

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 
... 

我搜索在網絡上的解決方案,與UNION的東西,但我不明白如何實現一個煤礦。希望可以有人幫幫我。

回答

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 
2

由於hellocode已與創建包含時間值的新表是一個很好的方式回答,這裏是另一種方式來實現這一目標使用聯合

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` 
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/