Gordon Linoff có câu trả lời dựa trên CTE
Tôi đã thực hiện một số phân tích hiệu suất trên tất cả các thuật toán đang hoạt động Giá trị thấp có nghĩa là nó mất quá nhiều thời gian. Điều này được thử nghiệm trên một chip Core i7 X920 @ 2GHz duy nhất, được hỗ trợ bởi một vài ổ SSD. Chỉ mục duy nhất được tạo là một cụm trên UserID, AvailStart. Nếu bạn nghĩ rằng bạn có thể cải thiện bất kỳ hiệu suất nào, hãy cho tôi biết.
Phiên bản CTE này tệ hơn tuyến tính, Máy chủ SQL không thể thực hiện kết nối RN =RN + 1 một cách hiệu quả. Tôi đã khắc phục điều này bằng một phương pháp kết hợp bên dưới, nơi tôi lưu và lập chỉ mục CTE đầu tiên vào một biến bảng. Điều này vẫn tốn IO gấp mười lần so với phương pháp dựa trên con trỏ.
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
Sau khi thực hiện một số phân tích hiệu suất, đây là phiên bản CTE / biến bảng kết hợp hoạt động tốt hơn bất kỳ thứ gì ngoại trừ phương pháp dựa trên con trỏ
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