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

複数の日付範囲間の分の合計

    Gordon Linoff CTEベースの回答 があります

    パフォーマンス分析 を行いました。 動作しているすべてのアルゴリズムで空白の値は、時間がかかりすぎたことを意味します。これは、2つのSSDに支えられた単一のCore i7 X920@2GHzチップでテストされています。作成された唯一のインデックスは、UserIDのクラスターであるAvailStartでした。パフォーマンスを改善できると思われる場合は、お知らせください。

    このCTEバージョンは線形よりも悪く、SQLServerは効率的な方法でRN=RN+1結合を実行できません。以下のハイブリッドアプローチでこれを修正しました。最初のCTEを保存し、テーブル変数にインデックスを付けます。これでも、カーソルベースのアプローチの10倍のIOが必要です。

    With OrderedRanges as (
      Select
        Row_Number() Over (Partition By UserID Order By AvailStart) AS RN,
        AvailStart,
        AvailEnd
      From
        dbo.Available
      Where
        UserID = 456
    ),
    AccumulateMinutes (RN, Accum, CurStart, CurEnd) as (
      Select
        RN, 0, AvailStart, AvailEnd
      From
        OrderedRanges
      Where 
        RN = 1
      Union All
      Select
        o.RN, 
        a.Accum + Case When o.AvailStart <= a.CurEnd Then
            0
          Else 
            DateDiff(Minute, a.CurStart, a.CurEnd)
          End,
        Case When o.AvailStart <= a.CurEnd Then 
            a.CurStart
          Else
            o.AvailStart
          End,
        Case When o.AvailStart <= a.CurEnd Then
            Case When a.CurEnd > o.AvailEnd Then a.CurEnd Else o.AvailEnd End
          Else
            o.AvailEnd
          End
      From
        AccumulateMinutes a
            Inner Join 
        OrderedRanges o On 
            a.RN = o.RN - 1
    )
    
    Select Max(Accum + datediff(Minute, CurStart, CurEnd)) From AccumulateMinutes 
    

    http://sqlfiddle.com/#!6/ac021/2

    パフォーマンス分析を行った後、カーソルベースのアプローチ以外の何よりも優れたパフォーマンスを発揮するハイブリッドCTE/テーブル変数バージョンを次に示します

    Create Function dbo.AvailMinutesHybrid(@UserID int) Returns Int As
    Begin
    
    Declare @UserRanges Table (
      RN int not null primary key, 
      AvailStart datetime, 
      AvailEnd datetime
    )
    Declare @Ret int = Null
    
    ;With OrderedRanges as (
      Select
        Row_Number() Over (Partition By UserID Order By AvailStart) AS RN,
        AvailStart,
        AvailEnd
      From
        dbo.Available
      Where
        UserID = @UserID
    )
    Insert Into @UserRanges Select * From OrderedRanges
    
    
    ;With AccumulateMinutes (RN,Accum, CurStart, CurEnd) as (
      Select
        RN, 0, AvailStart, AvailEnd
      From
        @UserRanges
      Where 
        RN = 1
      Union All
      Select
        o.RN, 
        a.Accum + Case When o.AvailStart <= a.CurEnd Then
            0
          Else 
            DateDiff(Minute, a.CurStart, a.CurEnd)
          End,
        Case When o.AvailStart <= a.CurEnd Then 
            a.CurStart
          Else
            o.AvailStart
          End,
        Case When o.AvailStart <= a.CurEnd Then
            Case When a.CurEnd > o.AvailEnd Then a.CurEnd Else o.AvailEnd End
          Else
            o.AvailEnd
          End
      From
        AccumulateMinutes a
            Inner Join 
        @UserRanges o On 
            a.RN + 1 = o.RN
    )
    
    Select 
      @Ret = Max(Accum + datediff(Minute, CurStart, CurEnd)) 
    From 
      AccumulateMinutes 
    Option
      (MaxRecursion 0)
    
    Return @Ret
    
    End
    

    http://sqlfiddle.com/#!6/bfd94



    1. 別のテーブルで定義されたマッピングに従ってテーブルの列の名前を変更する-MYSQLを使用

    2. OracleAQデキュー順序

    3. Doctrine 2mysqlFIELD関数を順番に

    4. to_sqlを使用してパンダデータフレームをOracleデータベースに書き込む方法は?