Merging unused timeslots

2019-08-17 18:41发布

问题:

I have a query result like

|----------------------------------------------|
|StaffId|BranchId|StartTime|EndTime |PatientId |
|----------------------------------------------|
|1      |1       |09:30:00 |09:35:00|Null      |
|1      |1       |09:35:00 |09:40:00|Null      |
|1      |1       |09:40:00 |09:55:00|1         |
|1      |1       |09:55:00 |10:00:00|Null      |
|1      |1       |10:00:00 |10:05:00|Null      |
|1      |1       |10:05:00 |10:20:00|2         |
|1      |1       |10:20:00 |10:25:00|NULL      |
|1      |1       |10:25:00 |10:40:00|3         |
|1      |1       |10:40:00 |10:45:00|Null      |
|1      |1       |10:45:00 |10:50:00|Null      |
|1      |1       |10:50:00 |10:55:00|Null      |
|1      |1       |10:55:00 |11:00:00|Null      |
|----------------------------------------------|

but I want to be able to group all the unused timeslots (those without a Patient Id) so the result is

|----------------------------------------------|
|StaffId|BranchId|StartTime|EndTime |PatientId |
|----------------------------------------------|
|1      |1       |09:30:00 |09:40:00|Null      |
|1      |1       |09:40:00 |09:55:00|1         |
|1      |1       |09:55:00 |10:05:00|Null      |
|1      |1       |10:05:00 |10:20:00|2         |
|1      |1       |10:20:00 |10:25:00|NULL      |
|1      |1       |10:25:00 |10:40:00|3         |
|1      |1       |10:40:00 |11:00:00|Null      |
|----------------------------------------------|

Not really sure how to go about doing this though, any guidance would be appreciated. Not sure if it's easier but the results of the query are got by joining a table with just the 5 minute intervals with the patients using the sql

INSERT INTO @Results(BranchId, StaffId, StartTime, EndTime)
SELECT us.BranchId, us.StaffId, us.StartTime, us.EndTime
FROM @UnusedSlots us
    left join @Results r
        on (NOT ((r.StartTime >= us.EndTime)
            OR (r.EndTime <= us.StartTime))) AND
            (us.StaffId = r.StaffId)
    where r.BranchId is Null

Where @UnusedSlots is the available 5 minute slots and @Results contains the booked slots (with the patient Ids). If there is a way to build the merging into this statement it would be much better.

I've thought I could use the Min(StartTime) and Max(EndTime) if I can isolate the adjoining results without a PatientId but I'm not sure how to do that, i keep ending up with one that has merged from 09:30 to 11:00

回答1:

select 
    StaffId,
    BranchId,
    MIN(StartTime),
    MAX(EndTime),
    PatientId
from
(
select *,       
    ROW_NUMBER() over (order by starttime) 
      - ROW_NUMBER() over (partition by isnull(PatientID,-1) order by starttime) rn     
from @results
) v
group by staffid, branchid, patientid, rn
order by MIN (starttime)

I'm not sure if StaffID and BranchID can vary in the @results table, so you may need to tweak this if they can.



回答2:

you could use a cursor, performance won't scale too well though:

    create table #tempTimes (
        staffid int,
        branchid int,
        startTime datetime,
        endTime datetime,
        patientid int)

    insert into #temptimes values (1,1,'2012-09-09 09:30', '2012-09-09 09:35', null)
    insert into #temptimes values (1,1,'2012-09-09 09:35', '2012-09-09 09:40', null)
    insert into #temptimes values (1,1,'2012-09-09 09:40', '2012-09-09 09:45', null)
    insert into #temptimes values (1,1,'2012-09-09 09:45', '2012-09-09 09:55', 1)
    insert into #temptimes values (1,1,'2012-09-09 09:55', '2012-09-09 10:00', null)
    insert into #temptimes values (1,1,'2012-09-09 10:00', '2012-09-09 10:05', null)
    insert into #temptimes values (1,1,'2012-09-09 10:05', '2012-09-09 10:20', 2)

    declare @currentPeriodStart datetime
    declare @currentPeriodEnd datetime
    declare @startTime datetime
    declare @endTime datetime
    declare @lastEndTime datetime
    declare @patientId int

    declare myCurs cursor for select startTime, endTime, patientid from #tempTimes order by startTime 

    open myCurs

    fetch next from myCurs into @startTime, @endTime, @patientId

    while @@fetch_status = 0
    begin
        if @patientId is null
        begin
            if (@currentPeriodStart is null)
                set @currentPeriodStart = @startTime

            set @currentPeriodEnd = @endTime
        end else
        begin
            print   cast(@currentPeriodStart as nvarchar) + ' to ' + cast(@currentPeriodEnd as nvarchar)
            set @currentPeriodStart = null
        end

        fetch next from myCurs into @startTime, @endTime, @patientId
    end

    close myCurs
    deallocate myCurs