2014-10-22 76 views
3

我碰到下面的問題就來了:MYSQL - 沁間隔日期

我想總結每個名稱的時間,給STARTEND活動之間的總間隔, 會簡單,如果我可以從各減記錄開始的結尾,比如瑪麗,從13歲開始到15歲,在14歲和16歲時開始另一項活動,我希望結果是3(她用了3個小時的時間來完成這兩項活動)

如:

Name | START    | END     | 
----------------------------------------------------------- 
KATE | 2014-01-01 13:00:00 | 2014-01-01 14:00:00 | 
MARY | 2014-01-01 13:00:00 | 2014-01-01 15:00:00 | 
TOM  | 2014-01-01 13:00:00 | 2014-01-01 16:00:00 | 
KATE | 2014-01-01 12:00:00 | 2014-01-02 04:00:00 | 
MARY | 2014-01-01 14:00:00 | 2014-01-01 16:00:00 | 
TOM  | 2014-01-01 12:00:00 | 2014-01-01 18:00:00 | 
TOM  | 2014-01-01 22:00:00 | 2014-01-02 02:00:00 | 

結果:

KATE 15 hours 
MARY 3 hours 
TOM  9 hours 
+0

使用TIMESTAMPDIFF函數可輕鬆完成每行的小時差異:TIMESTAMPDIFF(HOUR,'START','END')。在不同行中重疊間隔找到聯合是一個更難的問題。 – 2014-10-22 20:29:51

回答

1

您是否嘗試過一組通過,然後聚合函數?

SELECT Name, SUM(UNIX_TIMESTAMP(End) - UNIX_TIMESTAMP(Start)) FROM myTable 
GROUP BY Name 

這將從您有的間隔中返回累計總數秒。然後,您可以將秒數更改爲幾小時進行顯示。

此外,我強烈建議通過主鍵或其他東西而不是字符串名稱分組,但我明白,這可能只是爲了簡化問題。

0

我發現這個問題很有趣,所以花了一點時間來開發解決方案。我想到的是按名稱和開始時間對行進行排序,然後使用MySQL變量來說明重疊範圍。我首先排序表和與攜帶的名稱和時間從一行到下一

SELECT [expounded below] 
FROM (SELECT * FROM tbl ORDER BY Name, START, END) AS u, 
    (SELECT @x := 0, @gap := 0, @same_name:='', 
       @beg := (SELECT MIN(START) FROM tbl), 
       @end := (SELECT MAX(END) FROM tbl)) AS t 

這增加的名稱和時間範圍表中的每一行的外邊界列補充它,因爲以及排序表,以便 名稱按照開始時間順序排列。對於每一行,我們現在將擁有@same_name,@beg和@end,它們將值從一行轉移到下一行,@x和@gap將累積小時數。

現在我們必須對可能發生的重疊做一些推理。對於任何兩個間隔,它們或者是不相交的或具有交點:

Non-overlapping: beg--------end  START-------END 

Overlapping: beg-----------end        beg---------end 
        START--------------END   START-----------END 

Subset: beg---------------------------------end 
       START-----END 

一旦行是相鄰的,我們可以如果兩個範圍通過比較它們的起點和終點重合決定。它們重疊 如果一個的開始是另一個,反之亦然結束之前:

IF(@end >= START && @beg <= END, 

如果它們確實重疊,則總的時間間隔是兩個間隔的外邊緣之間的差異:

TIMESTAMPDIFF(HOUR, LEAST(@beg, START), GREATEST(@end, END)) 

如果它們不重疊,那麼我們可以將新間隔添加到前一個間隔。

我們還需要知道間隔之間的差距,這是從第一個結束到第二個開始的差異。這對於計算多於兩個時間間隔的情況下計算小時數是必要的,其中只有一些重疊。

1-----------2   3----------4 
         3--------------------5 

把此一起得到我們每行的計算,其中每行計算與它上面的一個 小時並集。對於每個變量,我們必須重新設置,如果名稱更改:

SELECT Name, START, END, 

    @x := IF(@same_name = Name, 
      IF(@end >= START && @beg <= END, -- does it overlap? 
       TIMESTAMPDIFF(HOUR, LEAST(@beg, START), GREATEST(@end, END)), 
       @x + TIMESTAMPDIFF(HOUR, START, END)), 
      TIMESTAMPDIFF(HOUR,START,END)) AS hr, 

    @gap := IF(@same_name = Name, 
       IF(@end >= START && @beg <= END, -- does it overlap? 
        @gap, 
        @gap + TIMESTAMPDIFF(HOUR, @end, START)), 
       0) AS gap, 

    @beg := IF(@same_name = Name, 
       CAST(LEAST(@beg, START) AS DATETIME), -- expand interval 
       START) AS beg,      -- reset interval 

    @end := IF(@same_name = Name, 
       CAST(GREATEST(@end, END) AS DATETIME), 
       END) AS finish, 
    @same_name := Name AS sameName 
FROM 
    (SELECT * FROM xt ORDER BY Name, START, END) AS u, 
    (SELECT @x := 0, @gap := 0, @same_name:='', @beg := (SELECT MIN(START) FROM xt), @end := (SELECT MAX(END) FROM xt)) AS t 

這仍然給了我們許多行,因爲在原始表。小時和縫隙會積聚每個名稱,所以我們必須通過名稱來選擇最高值和組:

SELECT Name, MAX(hr) - MAX(gap) AS HOURS 
FROM ([insert above query here]) AS intermediateCalculcation 
GROUP BY Name; 

編輯 ,當然按下回車鍵後片刻,它發生,我認爲(一)根本沒有重疊間隔的名稱存在一個錯誤;和(b)所有的@x真正在做的是建立從MIN(START)到MAX(END)的eacdh名稱的間隔,這可以通過更簡單的查詢和連接來完成。嗯,爲讀者鍛鍊? :-)

+0

這有點矯枉過正,因爲解決方案應該是一個簡單的聲明:) – Umingo 2014-10-23 21:00:32

+0

簡單的GROUP BY並不能解決問題,因爲它沒有解決重疊問題。例如,瑪麗的結果應該是3,因爲她有兩行重疊,從13到15和14到16.簡單的GROUP BY不考慮重疊和答案4。 – 2014-10-23 21:12:13