2015-05-09 41 views
4

我期待按小時收集計數。但不是每個小時都在我的桌子上。如何從表格中選擇小時計數,包括錯過時間?

爲了確保數據始終包含空閒時間,我建立了一個小時表,它的日期時間爲2000-2037。我想我可以將LEFT JOIN數據表格保存到這張表中,以跟蹤失蹤時間。但我需要幫助。

表:date_hour

`hour` 
2000-01-01 00:00:00 
2000-01-01 01:00:00 
... 
2036-12-31 23:00:00 

my_data

log_date    field1 
2015-05-01 00:31:00 1000 
2015-05-01 04:19:00 2000  
2015-05-01 05:19:00 1000 
2015-05-01 07:19:00 100 
2015-05-01 07:35:00 6000 

期望的結果:

hour     count 
2015-05-01 00:00:00 1 
2015-05-01 01:00:00 0 
2015-05-01 02:00:00 0 
2015-05-01 03:00:00 0 
2015-05-01 04:00:00 1 
2015-05-01 05:00:00 1 
2015-05-01 06:00:00 0 
2015-05-01 07:00:00 2 

MySQL的嘗試:

SELECT 
    dh.hour, 
    COUNT(md.*) AS count 
FROM 
    date_hour dh 
    LEFT JOIN my_data md ON dh.hour = ????md.log_date???? 
WHERE 
     dh.hour >= '2015-05-01' 
    AND dh.hour < '2015-05-02' 
GROUP BY 
    dh.hour 
ORDER BY 
    dh.hour; 

完成這些計數的最有效方法是什麼?假設每天有100k-1MM記錄,目標是一次測量至少30天的數據。

+2

可以留在加入'DATE_FORMAT(my_data.log_date,「%Y-%間%d%H:00 :00「)' – amdixon

+0

...並保留在開始表中只有24個recorss –

+0

您的SQL顯示正確。在dh.hour和md.log_date上創建索引並使用'where dh.hour> ='2015-05-01 00:00:00'和dh.hour <'2015-05-02 00:00:00''作爲表示日期/時間的一致性。 – zedfoxus

回答

3

可以使用DATE_FORMAT到剝去分和秒,如:

SELECT 
    dh.hour, 
    COUNT(md.*) AS count 
FROM 
    date_hour dh LEFT JOIN my_data md 
    ON dh.hour = DATE_FORMAT(md.log_date, "%Y-%m-%d %H:00:00") 
WHERE 
     dh.hour >= '2015-05-01' 
    AND dh.hour < '2015-05-02' 
GROUP BY 
    dh.hour 
ORDER BY 
    dh.hour 
; 

輸出

+------------------------+-----------+ 
|   hour   | count | 
+------------------------+-----------+ 
| 2015-05-01 00:00:00 | 1   | 
| 2015-05-01 01:00:00 | 0   | 
| 2015-05-01 02:00:00 | 0   | 
| 2015-05-01 03:00:00 | 0   | 
| 2015-05-01 04:00:00 | 1   | 
| 2015-05-01 05:00:00 | 1   | 
| 2015-05-01 06:00:00 | 0   | 
| 2015-05-01 07:00:00 | 2   | 
| ... trailing hours ... | allzeroes | 
+------------------------+-----------+ 

這裏的一切後2015年5月1日08

查詢: 00:00爲零(my_data中沒有數據)

sqlfiddle

+0

連接是否應該是LEFT OUTER JOIN,因此返回my_data表中有0條記錄的小時? – Zambonilli

1

如果LEFT JOINDATE_FORMAT功能或任何其他功能會產生正確的結果,但它可能是方式比它慢可能是結果。如果@amdixon的答案中顯示的簡單方法的性能是合適的,那麼就使用它。

但是,有幾件事情可以做得更快。一旦您的表增長到30M行(30天,每天1M行),您可能需要考慮它們。

毫無疑問,表date_hour必須在hour列上具有索引(實際上是主鍵)。這將有助於當你使用這樣的搜索條件來快速查找特定日子的幾行:

WHERE 
     date_hour.hour >= '2015-05-01 00:00:00' 
    AND date_hour.hour < '2015-05-02 00:00:00' 

要記住的另一個重要的事情 - 如果你有一個給定的一天1M行,你需要計算計數那一天,服務器必須至少讀取這些1M行。你無法避免這種情況。讀取1M行不會很快,但如果整個表是30M行,那麼讀取1M行比顯示整個表明顯更好。

因此,服務器應該能夠有效地查找特定日子的行(讀 - 應該有一個索引)。 任何查詢在加入時將從log_date列中刪除分鐘和秒數,但無法使用索引,因此服務器必須掃描整個表my_data

選項1

my_data添加的索引。 log_date。將明確的過濾器添加到WHERE子句。它不會改變結果,但希望能給服務器提供一個很好的提示來使用my_data上的索引。 log_date查找必要的行並避免完整掃描。如果你使用DATE_FORMATdatetime轉換爲字符串,並且它不會將date_hour.hour轉換爲字符串以進行比較(從而否定date_hour.hour上存在索引),那麼MySQL可能足夠智能。也許不會。我更喜歡以下方法從datetime中刪除分鐘和秒,而不將其轉換爲字符串。

TIMESTAMPADD(HOUR, 
    TIMESTAMPDIFF(HOUR,'2015-01-01 00:00:00',DateTimeValue), 
    '2015-01-01 00:00:00') 

只要沒有分鐘和秒,我們可以使用任何常數而不是'2015-01-01'。可以使用相同的方法將datetime截斷爲任何其他邊界 - 分,日,周,月,年。

SELECT 
    date_hour.hour, 
    COUNT(my_data.log_date) AS count 
FROM 
    date_hour 
    LEFT JOIN my_data ON 
     date_hour.hour = TIMESTAMPADD(HOUR, TIMESTAMPDIFF(HOUR,'2015-01-01 00:00:00',my_data.log_date), '2015-01-01 00:00:00') 
WHERE 
    date_hour.hour >= '2015-05-01 00:00:00' AND 
    date_hour.hour < '2015-05-02 00:00:00' AND 
    my_data.log_date >= '2015-05-01 00:00:00' AND 
    my_data.log_date < '2015-05-02 00:00:00' 
GROUP BY 
    date_hour.hour 
ORDER BY 
    date_hour.hour 
; 

即使服務器將使用上date_hourmy_data索引找到所需的行,它仍然有權加入基於函數的結果,並用1M行可能很難。最有可能的是,它必須將該函數的1M結果存儲到臨時表中,對其進行排序然後加入。這些種類通常很昂貴,特別是如果它們不在內存中(1M行很可能在磁盤上完成)。

選項2

爲了優化該進一步和避免在運行中,我會考慮增加一個持久柱log_hourmy_data表,這將連同主柱log_date填充和將所述datetime的操縱包含log_date值,無分鐘和秒。您可以將其視爲預先計算或緩存。一旦你在這個列上有索引log_hour服務器應該能夠有效地找到並加入找到的行。查詢變得微不足道,它不使用log_date列的話,那只有log_hour用途:

SELECT 
    date_hour.hour, 
    COUNT(my_data.log_hour) AS count 
FROM 
    date_hour 
    LEFT JOIN my_data ON date_hour.hour = my_data.log_hour 
WHERE 
    date_hour.hour >= '2015-05-01 00:00:00' AND 
    date_hour.hour < '2015-05-02 00:00:00' AND 
    my_data.log_hour >= '2015-05-01 00:00:00' AND 
    my_data.log_hour < '2015-05-02 00:00:00' 
GROUP BY 
    date_hour.hour 
ORDER BY 
    date_hour.hour 
; 
+0

非常聰明。也會研究這一點。謝謝。 – Ryan

相關問題