sql >> Database >  >> RDS >> Sqlserver

Som van minuten tussen meerdere perioden

Gordon Linoff heeft een CTE-gebaseerd antwoord

Ik heb wat prestatieanalyse gedaan op alle werkende algoritmenLege waarden betekenen dat het te lang duurde. Dit is getest op een enkele Core i7 X920 @2GHz-chip, ondersteund door een paar SSD's. De enige index die werd gemaakt, was een cluster op UserID, AvailStart. Als je denkt dat je een van de prestaties kunt verbeteren, laat het me dan weten.

Deze CTE-versie was erger dan lineair, SQL Server kan de RN =RN + 1 join niet op een efficiënte manier uitvoeren. Ik heb dit verholpen met een hybride benadering hieronder, waarbij ik de eerste CTE opsla en indexeer in een tabelvariabele. Dit kost nog steeds tien keer zoveel IO als de cursorgebaseerde benadering.

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

Na wat prestatieanalyse te hebben gedaan, is hier een hybride CTE/tabelvariabele versie die beter presteert dan wat dan ook, behalve de op cursor gebaseerde benadering

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 ConnectorJ met meerdere SQL-instructies

  2. Zorg voor PL/SQL-uitvoer in realtime

  3. MySQL-volgorde van bewerkingen?

  4. Tabellen zoeken die een specifieke kolom bevatten in SQL Server