Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Überlappende Zeitintervalle abflachen/zusammenführen

Ich habe mir nur eine CTE-Abfrage ausgedacht, da das Problem darin besteht, dass es möglicherweise eine Kette von sich überschneidenden Zeiten gibt, z. Datensatz 1 überschneidet sich mit Datensatz 2, Datensatz 2 mit Datensatz 3 und so weiter. Dies ist ohne CTE oder andere Schleifen usw. schwer zu lösen. Bitte versuchen Sie es trotzdem.

Der erste Teil der CTE-Abfrage ruft die Dienste ab, die eine neue Gruppe starten und nicht die gleiche Startzeit wie ein anderer Dienst haben (ich brauche nur einen Datensatz, der eine Gruppe startet). Der zweite Teil bekommt diejenigen, die eine Gruppe starten, aber es gibt mehr als eine mit der gleichen Startzeit - wieder brauche ich nur eine von ihnen. Der letzte Teil baut rekursiv auf der Startgruppe auf und nimmt alle überlappenden Dienste in Anspruch.

Hier ist SQLFiddle mit weiteren Datensätzen hinzugefügt, um verschiedene Arten von Überschneidungen und doppelten Zeiten zu demonstrieren.

Ich konnte ServiceID nicht verwenden da es genauso wie BeginTime bestellt werden müsste .

;with flat as
(
 select StaffID, ServiceDate, BeginTime, EndTime, BeginTime as groupid 
 from services S1
 where not exists (select * from services S2 
 where S1.StaffID = S2.StaffID 
 and S1.ServiceDate = S2.ServiceDate 
 and S2.BeginTime <= S1.BeginTime and S2.EndTime <> S1.EndTime
 and S2.EndTime > S1.BeginTime)

  union all

  select StaffID, ServiceDate, BeginTime, EndTime, BeginTime as groupid 
  from services S1
 where exists (select * from services S2 
 where S1.StaffID = S2.StaffID 
 and S1.ServiceDate = S2.ServiceDate 
 and S2.BeginTime = S1.BeginTime and S2.EndTime > S1.EndTime)
   and not exists (select * from services S2 
 where S1.StaffID = S2.StaffID 
 and S1.ServiceDate = S2.ServiceDate 
 and S2.BeginTime < S1.BeginTime
 and S2.EndTime > S1.BeginTime)

 union all

 select S.StaffID, S.ServiceDate, S.BeginTime, S.EndTime, flat.groupid 
 from flat
 inner join services S 
 on flat.StaffID = S.StaffID
 and flat.ServiceDate = S.ServiceDate
 and flat.EndTime > S.BeginTime
 and flat.BeginTime < S.BeginTime and flat.EndTime < S.EndTime
)

select StaffID, ServiceDate, MIN(BeginTime) as begintime, MAX(EndTime) as endtime 
from flat
group by StaffID, ServiceDate, groupid
order by StaffID, ServiceDate, begintime, endtime