簡體   English   中英

包括使用GROUP BY時丟失(零計數)行

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM