2014-02-19 34 views
0

我們有這樣的一個表:如何檢測MySQL DATETIME列中的連續小時數?

 
DESCRIBE time_slots; 

    id   int(11) 
    user_id  int(11) 
    start_time datetime 

start_time領域始終是一個小時的增量(例如2013-09-04 16:00:00

我們的數據科學家想要查詢該表中標識連續start_time記錄每user_id方式這樣,她可以創建一個派生表,看起來像這樣:

 
    id   int(11) 
    user_id  int(11) 
    start_time datetime 
    end_time datetime 

例如,給出這樣的數據:

 
user_id: 5, start_time: 2013-09-04 16:00:00 
user_id: 5, start_time: 2013-09-04 17:00:00 
user_id: 5, start_time: 2013-09-04 18:00:00 

user_id: 6, start_time: 2013-09-04 16:00:00 
user_id: 6, start_time: 2013-09-04 17:00:00 
user_id: 6, start_time: 2013-09-04 18:00:00 

user_id: 6, start_time: 2013-09-04 20:00:00 
user_id: 6, start_time: 2013-09-04 21:00:00 
user_id: 6, start_time: 2013-09-04 22:00:00 

...我們可以得出這樣的輸出:

 
user_id: 5, start_time: 2013-09-04 16:00:00, end_time: 2013-09-04 18:00:00 
user_id: 6, start_time: 2013-09-04 16:00:00, end_time: 2013-09-04 18:00:00 
user_id: 6, start_time: 2013-09-04 20:00:00, end_time: 2013-09-04 22:00:00 

有可能是每天給定用戶,這些開始/結束「塊」(但他們不會重疊)的倍數。

在我進入Plan B(設置非規範化數據倉庫)之前,有什麼想法可以在SQL中做到這一點?

回答

0

根據您的數據庫...窗口功能可以實現這一點。生成一個表示前一列的增量的列(因此,您需要按照user_id,startTime排序);然後,您可以使用該增量列來分組到組中。由於三角洲中的連續區塊將由「1」表示,並且新區塊將具有較高的數字。

也許你也可以通過與一個子選擇進行連接並使其偏移1來實現這一點,比如加入ROW_NUMBER和ROW_NUMBER-1,然後你可以計算時間戳之間的增量,並且用outerselect得到你想要的東西。關鍵是三角洲。

你可以做這樣的事情:

SET @prevUser := null; 
SET @prevStartTime := 0; 
SET @groupNumber := 1; 
SET @groupPrevUser := null; 


select 
    user, 
    groupNumber, 
    min(startTime), 
    max(endTime), 
    max(endTime) - min(startTime) as 'duration' 
from 
    (SELECT 
     user, 
      startTime, 
      endTime, 
      delta, 
      IF(delta != 10000 || @groupPrevUser <> user, @groupNumber:[email protected] + 1, @groupNumber) 'groupNumber', 
      @groupPrevUser:=user 
    from 
     (SELECT 
     user, 
      startTime, 
      endTime, 
      IF(@prevUser <> user || @prevStartTime = 0, endTime - startTime, startTime - @prevStartTime) AS delta, 
      @prevUser:=user, 
      @prevStartTime:=startTime 
    FROM 
     queries 
    ORDER BY user , startTime) userData) userGroupData 
group by user , groupNumber 

得到這樣的結果:

# user, groupNumber, min(startTime), max(endTime), duration 
bob, 1, 1392060000, 1392080000, 20000 
bob, 2, 1392090000, 1392100000, 10000 
jim, 3, 1392150000, 1392180000, 30000 

使用此基礎表:

# user, startTime, endTime 
bob, 1392060000, 1392070000 
bob, 1392070000, 1392080000 
bob, 1392090000, 1392100000 
jim, 1392150000, 1392160000 
jim, 1392160000, 1392170000 
jim, 1392170000, 1392180000 
+2

MySQL中不存在窗口函數。 –

+1

使用RANK和RANK-1或user_id進行自連接,starttime - 1小時作爲密鑰會給你同樣的能力 – mhoglan

+0

btw,InfiniDB是一個MySQL兼容數據庫(開源GPLv2),具有在標準MySQL上構建的窗口函數功能。 – mhoglan

0

我的第一個建議是,你改變模式爲block_by_id添加計數器。那麼你的問題是一個簡單的最小最大值。當創建記錄時,可以通過查看是否(1)此user_id已經有一條記錄,並且(2)它是否比新記錄早一個多小時,通過觸發器確定塊編號。我想你可以把它看作是非規範化的,在這種情況下,這個想法就是「快速地」找出塊。

SELECT user_id, MIN(start_time) AS start_time, MAX(start_time) AS start_time 
FROM time_slots t1 
WHERE NOT EXISTS 
    (SELECT 1 FROM time_slots AS t2 WHERE t1.user_id = t2.user_id 
     AND timestampdiff(HOUR, t1.start_time, t2.start_time)=1 
/* replace with date arithmetic function of your RDBMS if need be */ 
    ) 
GROUP BY user_id; 

我沒有任何經驗調整MySQL。可能不同的timediff表達式將允許它使用(user_id, start_time)上的索引。