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

Berechnung der SQL-Dauer

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:

  1. Erstellen Sie eine Tabelle mit allen Zeitbereichen von Interesse.
  2. Finden Sie die Startzeit für jede Gruppe von Zeitbereichen von Interesse.
  3. Finden Sie die Endzeit für jede Gruppe von Zeitbereichen von Interesse.
  4. 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