何度か、私は似たようなことをしました。基本的に、複雑な順序付け内の分離に基づくグループ化。この問題に関して、私が使用するアプローチの基本は次のとおりです。
<オール>または、より詳細には:(これらの各ステップは 1 つの大きな CTE の一部である可能性がありますが、読みやすいように一時テーブルに分割しています...)
ステップ 1:関心のあるすべての時間範囲のリストを見つけます (@Brad がリンクしている方法と同様の方法を使用しました)。 注:@Manfred Sorg が指摘したように、これはバスのデータに「欠落秒」がないことを前提としています。タイムスタンプに切れ目がある場合、このコードは 1 つの範囲を 2 つ (またはそれ以上) の異なる範囲として解釈します。
;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
プレ>ステップ 2:各停留所の最早時間を見つける
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
プレ>ステップ 3:各停留所の最新時刻を見つける
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
プレ>ステップ 4:すべてを結合する
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"
プレ>最後に、完成させるために整理します:
drop table #ends drop table #starts drop table #ranges
プレ>