sql >> Databáze >  >> RDS >> Sqlserver

Součet minut mezi více časovými obdobími

Gordon Linoffodpověď na základě CTE

Provedl jsem nějakou analýzu výkonu na všech pracovních algoritmechPrázdné hodnoty znamenají, že to trvalo příliš dlouho. To je testováno na jediném čipu Core i7 X920 @ 2 GHz, podporovaném několika SSD. Jediným vytvořeným indexem byl cluster s ID uživatele, AvailStart. Pokud si myslíte, že můžete zlepšit některý z výkonů, dejte mi vědět.

Tato verze CTE byla horší než lineární, SQL Server neumí spojení RN =RN + 1 efektivně. Napravil jsem to hybridním přístupem níže, kde uložím a indexuji první CTE do proměnné tabulky. To stále vyžaduje desetkrát více IO než přístup založený na kurzoru.

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

Po provedení analýzy výkonu je zde hybridní verze proměnné CTE/tabulka, která funguje lépe než cokoli kromě přístupu založeného na kurzoru

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. SQL dotaz pro stránkování s více sloupci; pochopit operátor OR

  2. Správa uživatelských účtů, role, oprávnění, autentizace PHP a MySQL - 3. část

  3. Uživatel nemůže vidět databáze v mysql workbench

  4. 2 způsoby, jak vrátit seznam propojených serverů v SQL Server pomocí T-SQL