2016-07-29 3 views
3

지금 당분간이 문제를 해결하려고했습니다. 많이 검색했지만 아무도 비슷한 문제가없는 것 같습니다. 여기두 테이블의 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 

나는이 두 테이블을 비교하고 일정 및 예약 시간 사이의 간격을 싶어. 예약되지 않은 시간. 쿼리의 속도

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 주간 간격이 있음). 하지만 쿼리 속도에 도움이되는 경우에만 그렇습니다.

미리 감사드립니다.

감사합니다, 아담 당신이 할 수있는

답변

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 쿼리를 만드는 훨씬 작은 제거하는 것입니다.

    성능 측면에서 temp 테이블에 allRows를 작성하고 인덱스를 찾아 쿼리 한 다음 쿼리를 작성하면 정말 빠릅니다.

  • +0

    이 스크립트는 정말 완벽합니다. 거의 99 %의 작업을 처리했습니다. 내가 언급 한 것을 잊어 버린 또 다른 문제가 있습니다. 때로는 일정이 8-10시에 시작하고 다른 일정은 10-12시에 시작됩니다.나는 스크립트를 실행하기 전에 그것들을 결합 할 수 있는지 궁금하다. – AdamG

    +0

    원래 스크립트에서 두 가지를 바꿀 필요가 있었다. 1 전혀 예약하지 않은 스케줄을 보여주지 않았다. 2 마지막 예약이 끝났을 때 작동하지 않았다. 일정이 (내 수정으로 스크립트가 느려지므로 대체 스크립트를 제공 할 수 있다면 감사하겠습니다. 중요하지는 않습니다.) – AdamG

    +0

    예약이없는 일정을 표시하도록 스크립트를 수정했습니다 (왼쪽 가입 그리고 여분의'ISNULL' 체크) 예약이 스케쥴의 끝 부분에있을 때 갭을 잡는 방법을 보여주었습니다. 그것은 모두 노조를 사용합니다 – Balah

    관련 문제