我發現這個問題很有趣,所以花了一點時間來開發解決方案。我想到的是按名稱和開始時間對行進行排序,然後使用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名稱的間隔,這可以通過更簡單的查詢和連接來完成。嗯,爲讀者鍛鍊? :-)
使用TIMESTAMPDIFF函數可輕鬆完成每行的小時差異:TIMESTAMPDIFF(HOUR,'START','END')。在不同行中重疊間隔找到聯合是一個更難的問題。 – 2014-10-22 20:29:51