Bei einer Reihe von Gelegenheiten habe ich etwas Ähnliches getan. Im Wesentlichen eine Gruppierung basierend auf Trennungen innerhalb einer komplexen Ordnung. Die Grundlagen des Ansatzes, den ich in Bezug auf dieses Problem verwende, sind wie folgt:
- Erstellen Sie eine Tabelle mit allen Zeitbereichen von Interesse.
- Finden Sie die Startzeit für jede Gruppe von Zeitbereichen von Interesse.
- Finden Sie die Endzeit für jede Gruppe von Zeitbereichen von Interesse.
- Verknüpfen Sie die Start- und Endzeiten mit der Liste der Zeitbereiche und der Gruppe.
Oder genauer gesagt:(Jeder dieser Schritte könnte Teil eines großen CTE sein, aber ich habe ihn zur leichteren Lesbarkeit in temporäre Tabellen heruntergebrochen...)
Schritt 1:Suchen Sie die Liste aller interessierenden Zeitbereiche (ich habe eine Methode verwendet, die der von @Brad verlinkten ähnelt). HINWEIS:Wie @Manfred Sorg betonte, setzt dies voraus, dass es in den Daten eines Busses keine "fehlenden Sekunden" gibt. Wenn es einen Bruch in den Zeitstempeln gibt, interpretiert dieser Code den einzelnen Bereich als zwei (oder mehr) unterschiedliche Bereiche.
;with stopSeconds as (
select BusID, BusStopID, TimeStamp,
[date] = cast(datediff(dd,0,TimeStamp) as datetime),
[grp] = dateadd(ss, -row_number() over(partition by BusID order by TimeStamp), TimeStamp)
from #test
where BusStopID is not null
)
select BusID, BusStopID, date,
[sTime] = dateadd(ss,datediff(ss,date,min(TimeStamp)), 0),
[eTime] = dateadd(ss,datediff(ss,date,max(TimeStamp)), 0),
[secondsOfStop] = datediff(ss, min(TimeStamp), max(Timestamp)),
[sOrd] = row_number() over(partition by BusID, BusStopID order by datediff(ss,date,min(TimeStamp))),
[eOrd] = row_number() over(partition by BusID, BusStopID order by datediff(ss,date,max(TimeStamp)))
into #ranges
from stopSeconds
group by BusID, BusStopID, date, grp
Schritt 2:Finden Sie die früheste Zeit für jede Haltestelle
select this.BusID, this.BusStopID, this.sTime minSTime,
[stopOrder] = row_number() over(partition by this.BusID, this.BusStopID order by this.sTime)
into #starts
from #ranges this
left join #ranges prev on this.BusID = prev.BusID
and this.BusStopID = prev.BusStopID
and this.sOrd = prev.sOrd+1
and this.sTime between dateadd(mi,-10,prev.sTime) and dateadd(mi,10,prev.sTime)
where prev.BusID is null
Schritt 3:Finden Sie die späteste Zeit für jede Haltestelle
select this.BusID, this.BusStopID, this.eTime maxETime,
[stopOrder] = row_number() over(partition by this.BusID, this.BusStopID order by this.eTime)
into #ends
from #ranges this
left join #ranges next on this.BusID = next.BusID
and this.BusStopID = next.BusStopID
and this.eOrd = next.eOrd-1
and this.eTime between dateadd(mi,-10,next.eTime) and dateadd(mi,10,next.eTime)
where next.BusID is null
Schritt 4:Alles zusammenfügen
select r.BusID, r.BusStopID,
[avgLengthOfStop] = avg(datediff(ss,r.sTime,r.eTime)),
[earliestStop] = min(r.sTime),
[latestDepart] = max(r.eTime)
from #starts s
join #ends e on s.BusID=e.BusID
and s.BusStopID=e.BusStopID
and s.stopOrder=e.stopOrder
join #ranges r on r.BusID=s.BusID
and r.BusStopID=s.BusStopID
and r.sTime between s.minSTime and e.maxETime
and r.eTime between s.minSTime and e.maxETime
group by r.BusID, r.BusStopID, s.stopOrder
having count(distinct r.date) > 1 --filters out the "noise"
Zum Schluss, um vollständig zu sein, räumen Sie auf:
drop table #ends
drop table #starts
drop table #ranges