重複を無視して、複数の日付範囲に何分あるかを調べる方法を尋ねる既存の質問があります。
与えられたサンプルデータは次のとおりです(ユーザーIDは特に関係ありません)
--Available--
ID userID availStart availEnd
1 456 '2012-11-19 16:00' '2012-11-19 17:00'
2 456 '2012-11-19 16:00' '2012-11-19 16:50'
3 456 '2012-11-19 18:00' '2012-11-19 18:30'
4 456 '2012-11-19 17:30' '2012-11-19 18:10'
5 456 '2012-11-19 16:00' '2012-11-19 17:10'
6 456 '2012-11-19 16:00' '2012-11-19 16:50'
カーソルを使用して問題を解決できますが、CTEに適応できるはずだと思いますが、その方法がわかりません。
その方法は、各範囲を開始時間で並べることです。次に、合体した範囲と重ならない範囲が見つかるまで、範囲を順番に合体する範囲を作成します。次に、合体した範囲に何分あるかを計算し、これを記憶して、次の範囲を続行し、重複する範囲を再度合体します。重複しない開始点を取得するたびに分を累積します 最後に、累積した分を最後の範囲の長さに追加します
順序のおかげで、範囲が以前の範囲とは異なる場合、開始日がすべて大きくなるため、範囲が以前の範囲と重複することはありません。
Declare
@UserID int = 456,
@CurStart datetime, -- our current coalesced range start
@CurEnd datetime, -- our current coalesced range end
@AvailStart datetime, -- start or range for our next row of data
@AvailEnd datetime, -- end of range for our next row of data
@AccumMinutes int = 0 -- how many minutes so far accumulated by distinct ranges
Declare MinCursor Cursor Fast_Forward For
Select
AvailStart, AvailEnd
From
dbo.Available
Where
UserID = @UserID
Order By
AvailStart
Open MinCursor
Fetch Next From MinCursor Into @AvailStart, @AvailEnd
Set @CurStart = @AvailStart
Set @CurEnd = @AvailEnd
While @@Fetch_Status = 0
Begin
If @AvailStart <= @CurEnd -- Ranges Overlap, so coalesce and continue
Begin
If @AvailEnd > @CurEnd
Set @CurEnd = @AvailEnd
End
Else -- Distinct range, coalesce minutes from previous range
Begin
Set @AccumMinutes = @AccumMinutes + DateDiff(Minute, @CurStart, @CurEnd)
Set @CurStart = @AvailStart -- Start coalescing a new range
Set @CurEnd = @AvailEnd
End
Fetch Next From MinCursor Into @AvailStart, @AvailEnd
End
Select @AccumMinutes + DateDiff(Minute, @CurStart, @CurEnd) As TotalMinutes
Close MinCursor
Deallocate MinCursor;
CTE が機能するようになりましたが、これは再帰のばかげたエラーでした。クエリ プランの急増は非常に印象的です。
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
これは CTE に適応できますか? また、この方法でリストを累積するための一般的なパターンはありますか?