sql >> Databasteknik >  >> RDS >> Sqlserver

Summan av minuter mellan flera datumintervall

Gordon Linoff har ett CTE-baserat svar

Jag har gjort en del prestandaanalys på alla fungerande algoritmerBlanka värden betyder att det tog för lång tid. Detta är testat på ett enda Core i7 X920 @2GHz-chip, uppbackat av ett par SSD-enheter. Det enda index som skapades var ett kluster på UserID, AvailStart. Om du tror att du kan förbättra något av prestandan, låt mig veta.

Denna CTE-version var sämre än linjär, SQL Server kan inte göra RN =RN + 1 join på ett effektivt sätt. Jag rättade till detta med en hybrid metod nedan, där jag sparar och indexerar den första CTE till en tabellvariabel. Detta kräver fortfarande tio gånger så mycket IO som den markörbaserade metoden.

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

Efter att ha gjort lite prestandaanalys, här är en hybrid CTE/tabellvariabelversion som presterar bättre än något annat förutom den markörbaserade metoden

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. hur man återställer identitetskolumnen i Oracle

  2. Villkorssortering i MySQL?

  3. PHP - PDO hämta resultatuppsättning med kolumn som index och kolumn som värde

  4. Postgresql SELECT om strängen innehåller