sql >> データベース >  >> RDS >> Sqlserver

SQL 継続時間の計算

    何度か、私は似たようなことをしました。基本的に、複雑な順序付け内の分離に基づくグループ化。この問題に関して、私が使用するアプローチの基本は次のとおりです。

    <オール>
  1. 関心のあるすべての時間範囲の表を作成します。
  2. 関心のある時間範囲の各グループの開始時間を見つけます。
  3. 関心のある時間範囲の各グループの終了時間を見つけます。
  4. 開始時刻と終了時刻を時間範囲のリストに結合し、グループ化します。
  5. または、より詳細には:(これらの各ステップは 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
      

    1. Doctrine 2 SUM()と同等のヘルパー?

    2. 複数のmysql結果を1つの列値でグループ化する

    3. Oracleで特定の文字列の言語(英語、中国語...)を判別する方法は?

    4. XamppとMySQLが起動していません