Sqlserver
 sql >> Teknologi Basis Data >  >> RDS >> Sqlserver

Jumlah menit antara beberapa rentang tanggal

Gordon Linoff memiliki jawaban berbasis CTE

Saya telah melakukan beberapa analisis kinerja pada semua nilai algoritma yang bekerjaBlank berarti terlalu lama. Ini diuji pada satu chip Core i7 X920 @2GHz, yang didukung oleh beberapa SSD. Satu-satunya indeks yang dibuat adalah cluster di UserID, AvailStart. Jika menurut Anda Anda dapat meningkatkan performa apa pun, beri tahu saya.

Versi CTE ini lebih buruk daripada linier, SQL Server tidak dapat melakukan RN =RN + 1 bergabung dengan cara yang efisien. Saya memperbaiki ini dengan pendekatan hybrid di bawah ini, di mana saya menyimpan dan mengindeks CTE pertama ke dalam variabel tabel. Ini masih membutuhkan IO sepuluh kali lebih banyak daripada pendekatan berbasis kursor.

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

Setelah melakukan beberapa analisis kinerja, inilah versi variabel CTE/tabel hybrid yang berkinerja lebih baik daripada apa pun kecuali pendekatan berbasis kursor

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. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Kueri SQL seperti GROUP BY dengan kondisi OR

  2. Bagaimana cara meminta nilai dan atribut Xml dari tabel di SQL Server?

  3. OrderBy di SQL Server untuk menempatkan nilai positif sebelum nilai negatif

  4. SQL untuk menemukan karakter non-numerik pertama dalam sebuah string

  5. Hapus catatan dalam Alih-alih Hapus pemicu