2016-07-29 121 views
3

我已經嘗試解決這個問題一段時間了。 我搜查了很多,但沒有人似乎有類似的問題。從兩個表中找出日期時間之間的時間差距

這裏是我的了:

Table 1/Schedule 
EmployeeID, PStart, PEnd 
1, 2016-07-01 08:00:00, 2016-07-01 12:00:00 
1, 2016-07-01 13:00:00, 2016-07-01 17:00:00 

Table 2/Bookings 
EmployeeID, PStart, PEnd 
1, 2016-07-01 08:00:00, 2016-07-01 08:40:00 
1, 2016-07-01 09:00:00, 2016-07-01 10:10:00 
1, 2016-07-01 10:30:00, 2016-07-01 11:00:00 
1, 2016-07-01 13:00:00, 2016-07-01 15:00:00 
1, 2016-07-01 15:00:00, 2016-07-01 15:30:00 

我想比較這兩個表,並得到安排和預約時間之間的差距。沒有預定的時間。

在這個例子中,這將是

Result table/Not booked 
EmployeeID, PStart, PEnd 
1, 2016-07-01 08:40:00, 2016-07-01 09:00:00 
1, 2016-07-01 10:10:00, 2016-07-01 10:30:00 
1, 2016-07-01 11:00:00, 2016-07-01 12:00:00 
1, 2016-07-01 15:30:00, 2016-07-01 17:00:00 

查詢的速度是非常重要的。員工數超過150人,所以有很多行。 我們將要使用的日期範圍可能會受到限制(例如,獲得兩週的差距),而不是顯示自開始以來的所有差距。但只有這有助於查詢速度。

在此先感謝。

問候, 亞當

回答

2

你可以做到這一點與熱膨脹係數的組合和ROW_NUMBER()窗函數。

的想法:

  • 組您預訂按計劃,
  • 給每個預訂的時間表越來越行號。這樣可以讓您將每個預訂加入下面的行。
  • 從那裏您可以選擇日期,並比較上次預訂的結束日期和下次預訂的開始日期,以查看日期是否觸及。
  • 交換日期,各地選擇要看到差距

在這裏的示例代碼使用上面的數據來做到這一點:

;with allRows as 
(
    -- give each booking an increasing row 
    select ROW_NUMBER() OVER (PARTITION BY scheduleRow ORDER BY scheduleRow, b.PStart, b.PEnd) bookingRow, 
     s.EmployeeId, 
     s.scheduleRow, 
     s.PStart as scheduleStart, 
     s.PEnd as scheduleEnd, 
     b.PStart as bookingStart, 
     b.PEnd as bookingEnd 
    from 
     (
      -- we need to add an id for our schedules (does it exist in your case?) 
      select ROW_NUMBER() OVER (ORDER BY PStart, PEnd) scheduleRow, * 
      FROM schedule 
     ) s 
     left join bookings b on -- so we can get schedules without bookings 
     s.employeeID = b.employeeID AND 
     s.PStart <= b.PEnd AND 
     s.PEnd >= b.PStart 
) 
select 
    bookingLeft.EmployeeId, 
    ISNULL(bookingLeft.bookingEnd, bookingLeft.scheduleStart) as PStart, 
    ISNULL(bookingRight.bookingStart, bookingLeft.scheduleEnd) as PEnd 
from allRows bookingLeft 
left join allRows bookingRight on 
    -- this joins the row to the one BELOW for the schedule 
    bookingLeft.scheduleRow = bookingRight.scheduleRow and 
    bookingLeft.bookingRow = bookingRight.bookingRow - 1 
where 
    -- this finds our gaps because the end of the last booking 
    -- doesn't touch the start of the next booking. 
    ISNULL(bookingLeft.bookingEnd, bookingLeft.scheduleStart) < ISNULL(bookingRight.bookingStart, bookingLeft.scheduleEnd) 

編輯:這並不滿足當預訂是在一個時間表的結尾。因爲不同的領域將被選中,排序的方法是使用UNION。因此,可以將其添加到主要腳本中,以考慮最終的預訂。

union all -- a bit quicker than a straight UNION 
select 
    bookingLeft.EmployeeId, 
    ISNULL(bookingRight.bookingEnd, bookingLeft.scheduleStart), 
    bookingLeft.bookingStart 
from allRows bookingLeft  
left join allRows bookingRight on 
    -- this joins the row to the one ABOVE for the schedule 
    bookingLeft.scheduleRow = bookingRight.scheduleRow and 
    bookingLeft.bookingRow = bookingRight.bookingRow + 1 
where 
    ISNULL(bookingRight.bookingEnd, bookingLeft.scheduleStart) < bookingLeft.bookingStart and 
    bookingLeft.bookingEnd >= bookingLeft.scheduleEnd -- special case when booking at the end of a schedule 

如果您使用SQL 2012及以上版本,你可以使用LEAD(),並且將消除allRows CTE使得查詢小得多。

至於性能,您可以將allRows寫入臨時表,對一個索引打耳光,然後從中進行查詢 - 它應該非常快。

+0

這個腳本非常完美,它完成了將近99%的工作。 我有另一個問題,我忘了提及。有時一個時間表從8到10開始,然後另一個時間表從10到12開始。我想知道是否可以在運行腳本之前加入它們 – AdamG

+0

我需要在原始腳本中更改兩件事 1它沒有顯示時間表,沒有任何預訂 2當最後一次預訂到最後時的時間表 (如果您有時間,如果您有時間可以提供替代腳本,我將不勝感激,因爲我的修復使腳本變慢,但並不重要) – AdamG

+0

我修改了腳本以顯示沒有預約的時間表(左連接和一個額外的'ISNULL'檢查),並顯示如何在預訂結束時獲得空白。它使用工會全部 – Balah