2017-03-06 31 views
2

我正在構建一個調度系統來跟蹤專業人員的時間表和可用性。用戶應該能夠輸入一些標準並查看專業人員下一次可用預約時間的最佳匹配專業人員列表。查詢返回這些專業人員的工作,但隨着管理專業人員數量的增長,返回結果所需的時間變得荒謬。找到下一個可用的專業人員

該系統工作過3個表的:專業定義了專業人士,可用性定義的可用性塊相關專業,預約規定安排相關專業的約會。因此,一些樣本數據可能看起來像:

INSERT INTO professional (id, name) 
VALUES 
    (1, 'Bob'), 
    (2, 'Frank'), 
    (3, 'Joe'); 

INSERT INTO availability (id, professional_id, start_date_time, end_date_time) 
VALUES 
    (1, 1, '03/06/2017 09:00:00', '03/06/2017 12:30:00'), 
    (2, 1, '03/06/2017 13:30:00', '03/06/2017 18:00:00'), 
    (3, 2, '03/06/2017 10:00:00', '03/06/2017 14:00:00'), 
    (4, 3, '03/07/2017 08:30:00', '03/07/2017 16:30:00'); 

INSERT INTO appointment (id, professional_id, start_date_time, end_date_time) 
VALUES 
    (1, 1, '03/06/2017 09:00:00', '03/06/2017 09:30:00'), 
    (2, 1, '03/06/2017 10:00:00', '03/06/2017 10:30:00'), 
    (3, 2, '03/06/2017 10:00:00', '03/06/2017 10:30:00'), 
    (4, 2, '03/06/2017 10:30:00', '03/06/2017 11:00:00'), 
    (5, 2, '03/06/2017 11:00:00', '03/06/2017 11:30:00'); 

查詢應返回的線沿線的一個結果:

name | next_availability 
----- | ----------------- 
Bob | 03/06/2017 09:30:00 
Frank | 03/06/2017 11:30:00 
Joe | 03/07/2017 08:30:00 

我使用Technet找到方法(有一些修改,以與合作我的實際設置)找到下一個可用性,但正如我所說的,隨着專業人員數量及其可用性和約會數量的增加,返回結果所需的時間變得不合理。

另外還有一個要求,爲了讓這更加困難,結果不應該包括任何即將到來的可用性的專業人員。

正如我所看到的,瓶頸在於,所有這些專業人員都需要計算下一個可用性。我考慮制定一份可用的預約表格,但維護該表格會令人望而卻步。關於如何有效地完成這項工作的任何想法?

非常感謝您的幫助。

編輯成包括WORKING QUERY:

DECLARE @buffer tinyint; 
DECLARE @duration tinyint; 

SET @buffer = 15; 
SET @duration = 30; 

WITH CTE 
AS (
    SELECT timeSlots.*, RowNumber = ROW_NUMBER() OVER(ORDER BY start_date_time ASC) 
    FROM (
      -- Create an "appointment" to define the start of the block of availability. 
      -- If the start of the availability is still in the future, use the actual start_date_time 
      -- as both the start and end_date_time of the "appointment". If not, calculate the first 
      -- possible availability within this block based on the current time, a number of @buffer 
      -- minutes (to allow the user time to complete booking, etc., and increments based on the 
      -- duration of appointments (e.g., if current time + @buffer is 11:32, but appointments are 
      -- 15 minutes each on the hour, round up to 11:45). 
      -- Only look up to 2 weeks out. 
      SELECT 
       availability.id, 
       'Start' AS type, 
       availability.professional_id, 
       availability.start_date_time, 
       Iif(availability.start_date_time > DateAdd(Minute, @buffer, GetDate()), availability.start_date_time, DateAdd(Minute, (@duration - (DateDiff(Minute, availability.start_date_time, DateAdd(Minute, DateDiff(Minute, 0, DateAdd(Second, 30, DateAdd(Minute, @buffer, GetDate()))), 0)) % @duration)), DateAdd(Minute, DateDiff(Minute, 0, DateAdd(Second, 30, DateAdd(Minute, @buffer, GetDate()))), 0))) AS end_date_time 
      FROM 
       availability 
      WHERE 
       availability.end_date_time > GetDate() 
       AND availability.end_date_time <= DateAdd(Week, 2, GetDate()) 

     UNION 

      -- Create an "appointment" to define the end of the block of availability. 
      -- Only look up to 2 weeks out. 
      SELECT 
       availability.id, 
       'End' AS type, 
       availability.professional_id, 
       availability.end_date_time as start_date_time, 
       availability.end_date_time AS end_date_time 
      FROM 
       availability 
      WHERE 
       availability.end_date_time > GetDate() 
       AND availability.end_date_time <= DateAdd(Week, 2, GetDate()) 

     UNION 

      -- Get alreasy scheduled appointments up to 2 weeks out. 
      SELECT 
       appointment.id, 
       'Appointment' AS type, 
       appointment.professional_id, 
       appointment.start_date_time, 
       appointment.end_date_time 
      FROM 
       appointment 
      WHERE 
       start_date_time >= GetDate() 
       AND start_date_time <= DateAdd(Week, 2, GetDate()) 
    ) AS timeSlots 
) 
SELECT 
    TOP 5 
    a.professional_id, 
    min(a.end_date_time) AS next_availability 
FROM 
    CTE a 
    INNER JOIN CTE b 
     ON a.RowNumber = b.RowNumber - 1 
     AND a.professional_id = b.professional_id 
WHERE 
    dateDiff(Minute, a.end_date_time, b.start_date_time) >= @duration 
    -- Restrict results to those where the start of the gap is at least @buffer away from current time 
    AND a.end_date_time >= DateAdd(Minute, @buffer, GetDate()) 
    AND a.type <> 'End' 
GROUP BY 
    a.professional_id 
ORDER BY 
    next_availability ASC 

編輯,以說明什麼WORKING QUERY DOES:

的CTE在上面的查詢生成一個表,其基本上包括每1行預約預約。計劃是找出這些任命之間有足夠的時間差距。爲了創建這些約會的邊界,還根據約會發生的可用性塊的開始和結束日期/時間,包括「開始」約會和「結束」約會。爲了確保沒有包含已經經過的時間間隔,如果可用性的start_date_time爲0,那麼「Start」的start_date_time爲可用性的start_date_time或當前日期和時間(調整爲下一個時間槽的start_date_time)已經過去了。

舉個例子,上面給出的樣本數據中,CTE將返回以下(表明專業的名稱,而不是ID):

id | type  | name | start_date_time  | end_date_time 
--- | ----------- | ----- | ------------------- | ------------------- 
1 | Start  | Bob | 03/06/2017 09:00:00 | 03/06/2017 09:00:00 
1 | Appointment | Bob | 03/06/2017 09:00:00 | 03/06/2017 09:30:00 
1 | Appointment | Bob | 03/06/2017 10:00:00 | 03/06/2017 10:30:00 
1 | End   | Bob | 03/06/2017 12:30:00 | 03/06/2017 12:30:00 
2 | Start  | Bob | 03/06/2017 13:30:00 | 03/06/2017 13:30:00 
2 | End   | Bob | 03/06/2017 18:00:00 | 03/06/2017 18:00:00 
3 | Start  | Frank | 03/06/2017 10:00:00 | 03/06/2017 10:00:00 
3 | Appointment | Frank | 03/06/2017 10:00:00 | 03/06/2017 10:30:00 
4 | Appointment | Frank | 03/06/2017 10:30:00 | 03/06/2017 11:00:00 
5 | Appointment | Frank | 03/06/2017 11:00:00 | 03/06/2017 11:30:00 
3 | End   | Frank | 03/06/2017 14:00:00 | 03/06/2017 14:00:00 
4 | Start  | Joe | 03/07/2017 08:30:00 | 03/07/2017 08:30:00 
4 | End   | Joe | 03/07/2017 16:30:00 | 03/07/2017 16:30:00 

鑑於上述CTE的結果,你可以看到,鮑勃開始工作9月3日9:00,當天有兩次預約,一次是9:00-9:30,另一次是10:00-10:30。什麼外部查詢正在做的是採取上述表,並將其加入到自身,由一個行偏移,這樣從上面看起來像鮑勃的數據(所有日期是3/06):

a.type | a.start | a.end | b.type | b.start | b.end 
------ | -------- | -------- | ------ | -------- | -------- 
Start | 09:00:00 | 09:00:00 | Appt | 09:00:00 | 09:30:00 
Appt | 09:00:00 | 09:30:00 | Appt | 10:00:00 | 10:30:00 
Appt | 10:00:00 | 10:30:00 | End | 12:30:00 | 12:30:00 
End | 12:30:00 | 12:30:00 | Start | 13:30:00 | 13:30:00 

外部查詢然後過濾這些結果,僅返回a.end和b.start之間的差至少持續時間長的那些行。第一行不起作用,因爲第一行的a.end(9:00)和第一行的b.start(9:00)之間的差異小於持續時間。第二行確實工作,因爲第二行的a.end(9:30)和第二行的b.start(10:00)之間的差異具有足夠的持續時間。通過這種方式,外部查詢僅返回那些持續時間足夠長的時間段,然後僅返回每個專業人員的第一個時間段。

+0

因此,一個專業是可用的(他的一般可用性期間),如果對於一個給定的任命沒有其他約會從第一個結束? – JimmyB

+1

什麼是輸入?例如,我需要在某天預約45分鐘,或者需要在某天某天從9:00-9:45進行預約。編輯:另外,它可以成爲使用datediff()與大量記錄慢得出名。 – justiceorjustus

+0

你看過你的執行計劃並建立了一些索引嗎? – Hogan

回答

0

確定這裏你去:

DECLARE @buffer tinyint; 
DECLARE @duration tinyint; 

SET @buffer = 15; 
SET @duration = 30; 

WITH TheDates AS 
(
    SELECT 
    availability.id, 
    availability.professional_id, 
    availability.start_date_time, 
    Iif(availability.start_date_time > DateAdd(Minute, @buffer, GetDate()), availability.start_date_time, DateAdd(Minute, (@duration - (DateDiff(Minute, availability.start_date_time, DateAdd(Minute, DateDiff(Minute, 0, DateAdd(Second, 30, DateAdd(Minute, @buffer, GetDate()))), 0)) % @duration)), DateAdd(Minute, DateDiff(Minute, 0, DateAdd(Second, 30, DateAdd(Minute, @buffer, GetDate()))), 0))) AS end_date_time, 
    availability.end_date_time AS end_date_time_real 
    FROM availability 
    WHERE 
    availability.end_date_time > GetDate() 
    AND availability.end_date_time <= DateAdd(Week, 2, GetDate()) 
), dates_filtered AS 
(
    SELECT 
    id, 
    professional_id, 
    start_date_time, 
    end_date_time, 
    end_date_time_real 
    FROM TheDates 
    WHERE dateDiff(Minute, end_date_time, start_date_time) >= @duration 
    AND end_date_time >= DateAdd(Minute, @buffer, GetDate()) 
), existingApts AS 
(
    -- Get alreasy scheduled appointments up to 2 weeks out. 
    SELECT 
    appointment.id, 
    appointment.professional_id, 
    appointment.start_date_time, 
    appointment.end_date_time 
    FROM appointment 
    WHERE 
    start_date_time >= GetDate() 
    AND start_date_time <= DateAdd(Week, 2, GetDate()) 
), dates_without_apt AS 
(-- filter out items with apt between start and end or starting or ending. 
    SELECT 
    id, 
    professional_id, 
-- start_date_time, 
-- end_date_time, 
    end_date_time_real 
    FROM dates_filtered D 
    LEFT JOIN existingApts A ON 
     -- apt between 
     (D.start_date_time <= A.start_date_time AND D.end_date_time_real >= A.end_date_time) OR 
     -- apt ends in range 
     (A.end_date_time >= D.start_date_time AND A.end_date_time <= D.end_date_time_real) OR 
     -- apt starts in range 
     (A.start_date_time >= D.start_date_time AND A.start_date_time <= D.end_date_time_real) OR 
    WHERE A.id is null 
) 
SELECT 
    TOP 5 
    a.professional_id, 
    min(a.end_date_time_real) AS next_availability 
FROM dates_without_apt a 
ORDER BY start_date_time ASC 
+0

謝謝,但這是行不通的。正如我所看到的,dates_without_apt返回沒有安排applet的可用性塊。但那不是我所追求的。根據availability_start和appt_start,appt_end和appt_start(下一個appt)以及appt_end和availability_end之間的差距,我需要知道至少持續幾分鐘的第一個可用時間。看看我發佈的樣本數據,Bob的第一個可用性是2017年3月3日9:30,因爲即使該塊在9:00開始,從9-9:30有一個appt,使得9:30成爲第一個可用性。 – queevert