2016-07-29 14 views
3

私はこの問題をしばらく解決しようとしました。 私は多くを検索しましたが、誰も同様の問題を抱えているようです。ここで2つのテーブルからdatetimeの時間間隔を見つけよう

は、私が得たものである:

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 

私はこの2つのテーブルを比較し、スケジュールと予約した時間との間のギャップを取得したいです。その時間は予約されていませんでした。

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週間のギャップが生じるなど)。しかし、それがクエリの速度に役立つ場合にのみ。

ありがとうございます。あなたはCTEのとROW_NUMBER()ウィンドウ関数の組み合わせでそれを行うことができ

よろしく、 アダム

答えて

2

アイデア:スケジュールによって

  • グループあなたの予約を、
  • は、スケジュール内の各予約に増加行番号を与えます。これにより、各予約を下の行に参加させることができます。
  • そこから日付を選択して、最後の予約の終了日と次の予約の開始日の比較を行い、日付が合っているかどうかを確認することができます。
  • スワップ周り日付ギャップ

はここより上のデータを使用してそれを行うためのサンプルコードです見に選択するとき:

;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) 

EDIT:これは予約時に応じるいませんスケジュールの終わりです。異なるフィールドが選択されるので、それを並べ替える方法は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を排除するであろう。

パフォーマンスに関しては、tempテーブルにallRowsを書き込み、インデックスにスラップしてからクエリを実行することができます。これは本当にすばらしいものです。

+0

このスクリプトは本当に完璧です、それは仕事のほぼ99%をしました。 私は言及するのを忘れた別の問題があります。場合によっては、スケジュールが8〜10で開始され、別のスケジュールが10〜12で開始されることもあります。スクリプトを実行する前にそれらに参加することができるのだろうか – AdamG

+0

元のスクリプトで2つのものを変更する必要があった 1予約していないスケジュールが全く表示されなかった 2最後の予約が最後にあったときスケジュールの (私の修正によりスクリプトが遅くなりましたが、重要ではないので、時間があれば代替スクリプトを提供できるかどうか分かります) – AdamG

+0

予約なしでスケジュールを表示するスクリプトを修正しましたそして特別なISNULLチェック)、予約がスケジュールの終わりにあるときにどのようにギャップを取るかを示しました。それはすべてのユニオンを使用します – Balah

関連する問題