2014-10-26 65 views
2

我想僅使用SQL從分組時間間隔集合(或時間段,這可能是適當的詞)中選擇公共重疊時間間隔。重疊時間間隔:選擇「所有繁忙」時段

真實世界的場景是一個呼叫中心,3 + 職位可以接聽電話。職位由特定的服務代表填寫,他們的職位分配隨時間而改變,但與此問題無關。我們可以假設,對於一個給定的位置,它總是由某人填充。

位置數隨時間變化緩慢。我試圖概括解決方案,以便它可以處理任意數量的職位。

輸入數據是一組調用,它們指向一個位置並具有開始時間和結束時間。顯然,給定的位置不能與自身重疊呼叫(假設一次只能進行一次呼叫),但其呼叫可以與一個或多個對其他位置的呼叫時間重疊。

問題是從呼叫數據中識別出所有位置正在通話中的所有時間間隔,因此呼叫中心無法應答該時間段內的任何新來話呼叫(「所有位置忙」)。

例如,對於三個位置(編號爲1,2,3)

Call Position CallStartTime  CallEndTime 

1  1   2014-01-01 14:01 2014-01-01 14:33  <--Comprises all busy intervals 1 and 2 
2  1   2014-01-01 14:45 2014-01-01 14:47 
3  1   2014-01-01 14:53 2014-01-01 14:57 
4  2   2014-01-01 13:01 2014-01-01 13:53  
5  2   2014-01-01 13:55 2014-01-01 14:25  <--comprises all busy interval 1 
6  2   2014-01-01 14:27 2014-01-01 14:29  <--comprises all busy interval 2 
7  2   2014-01-01 14:35 2014-01-01 14:41 
8  3   2014-01-01 14:21 2014-01-01 15:03  <--comprises all busy intervals 1 and 2 
9  3   2014-01-01 16:01 2014-01-01 16:11 

對於測試數據上面有當所有的位置都忙(重疊的所有位置的呼叫的不同的情況)的兩個時間間隔: 14:21 - 14:25和14:27 - 14:29。

所以所需的結果集將是

AllBusyStartTime AllBusyEndTime 
2014-01-01 14:21 2014-01-01 14:25 
2014-01-01 14:27 2014-01-01 14:29 

你看到,一個呼叫可具有與其它呼叫多個重疊(例如,呼叫位置1 14:01-14:33重疊,兩個呼叫位置2 13 :55-14:25和呼叫位置2 14:27-14:29)。

當a.StartTime < b.EndTime和a.EndTime> = b.StartTime時,兩個時間間隔(a,b)重疊。

如果我可以得到所有位置都有重疊的呼叫時間間隔集合,則相關的「全部忙」時間間隔由該集合中的最新(最近的)StartTime和最小(最早的)EndTime組成。

爲了更接近解決方案,我正在尋找一種通用算法來確定n個時間間隔相互重疊的時間。對於間隔a,b,c選擇重疊b和重疊c沒有足夠的限制。 A可能與b重疊,但b可能不會與c重疊,並且您需要所有間隔相互重疊。

我正在使用SQL Server進行測試。我嘗試過搜索網頁,但沒有發現任何能覆蓋這種情況的內容(大量關於兩個重疊時間間隔的簡單案例的討論)。我會分享SQL,但我仍然試圖找出「方法」,這是需要照亮的。

儘管我只有SQL-Server用於測試,但我仍希望儘可能保持解決方案的一般性,因爲它可能不會在SQL Server上實現。

+0

你正在使用什麼版本的SQL Server? – 2014-10-26 20:08:12

+0

SQL Server 2014. – Bes 2014-10-26 20:55:32

+0

在我的示例中,我使用「Call」作爲主鍵列的名稱,但它可能應該與「CallId」類似,因爲「Call」似乎是SQL Server中的保留字。 – Bes 2014-10-27 04:31:35

回答

2

讓我們來看看,隨時獲取同時通話的數量。該方法是獲取時間列表,其中用於開始呼叫的+1和用於呼叫結束的-1。下面給出了每個時間段的計數:

select thetime, sum(incall) over (order by thetime, call) as simultaneouscalls 
from ((select CallStartTime as thetime, call, +1 as incall 
     from calls 
    ) union all 
     (select CallEndTime, call, -1 as incall 
     from calls 
    ) 
    ) c; 

接下來,你想要的時期,所以用lead()得到一段被同時呼叫的數量到底,然後順序:

with c as (
     select thetime, sum(incall) over (order by thetime, call) as simultaneouscalls 
     from ((select CallStartTime as thetime, call, +1 as incall 
      from calls 
      ) union all 
      (select CallEndTime, call, -1 as incall 
      from calls 
      ) 
      ) c 
    ) 
select thetime, endtime, simultaneouscalls 
from (select c.*, lead(thetime) over (order the thetime) as endtime 
     from c 
    ) c 
order by simultaneouscalls, thetime; 

如果你真的想只有最大,那麼這個where子句添加到外查詢:

where simultaneouscalls = (select count(distinct position) from calls) 

注:本使用可用的構建在SQL Server 2012+而不是早期版本(因爲我寫這個沒有指示版本)。

+0

喬治我認爲該解決方案的作品!唯一的問題是我只能理解它是如何工作的,但我會努力工作(我不熟悉OVER和LEAD子句)。 (順便說一句,我認爲你在你的SQL中有一個小的錯字:我認爲你的意思是按順序排列順序)。非常感謝您的快速回復! – Bes 2014-10-27 04:20:11

+0

我打算確定的另一件事是,如果我可以在Oracle中實現這一點,因爲我預計目標平臺。 – Bes 2014-10-27 04:33:54

+0

@Bes。 。 。你應該正確地標記你的問題。在任何情況下,Oracle都支持比SQL Server更豐富的窗口函數,所以這也適用於此。 – 2014-10-27 11:25:42