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

Paket berbasis set berjalan lebih lambat daripada fungsi bernilai skalar dengan banyak kondisi

Istilah kata kunci di sini adalah INLINE FUNGSI TABEL BERHARGA . Anda memiliki dua jenis fungsi T-SQL yang dinilai dengan tabel:multi-pernyataan dan sebaris. Jika fungsi T-SQL Anda dimulai dengan pernyataan BEGIN maka itu akan menjadi omong kosong - skalar atau sebaliknya. Anda tidak bisa memasukkan tabel temp ke sebaris fungsi bernilai tabel jadi saya berasumsi Anda beralih dari skalar ke fungsi bernilai tabel pernyataan mutli yang mungkin akan lebih buruk.

Fungsi nilai tabel sebaris Anda (iTVF) akan terlihat seperti ini:

CREATE FUNCTION [dbo].[Compute_value]
(
  @alpha FLOAT,
  @bravo FLOAT,
  @charle FLOAT,
  @delta FLOAT
)
RETURNS TABLE WITH SCHEMABINDING AS RETURN
SELECT newValue = 
  CASE WHEN @alpha IS NULL OR @alpha = 0 OR @delta IS NULL OR @delta = 0 THEN 0
       WHEN @bravo IS NULL OR @bravo <= 0 THEN 100
       ELSE @alpha * POWER((100 / @delta), 
             (-2 * POWER(@charle * @bravo, DATEDIFF(<unit of measurement>,GETDATE(),'1/1/2000')/365)))
  END
GO;

Perhatikan bahwa, dalam kode yang Anda poskan, DATEDIFF . Anda pernyataan tidak memiliki datepart parameter. Jika harus terlihat seperti:

@x int = DATEDIFF(DAY, GETDATE(),'1/1/2000')   

Melangkah lebih jauh - penting untuk memahami mengapa iTVF lebih baik daripada fungsi yang ditentukan pengguna bernilai skalar T-SQL. Bukan karena fungsi bernilai tabel lebih cepat daripada fungsi bernilai skalar, itu karena implementasi fungsi inline T-SQL Microsoft lebih cepat daripada implementasi fungsi T-SQL yang tidak inline. Perhatikan tiga fungsi berikut yang melakukan hal yang sama:

-- Scalar version
CREATE FUNCTION dbo.Compute_value_scalar
(
  @alpha FLOAT,
  @bravo FLOAT,
  @charle FLOAT,
  @delta FLOAT
)
RETURNS FLOAT
AS
BEGIN
    IF @alpha IS NULL OR @alpha = 0 OR @delta IS NULL OR @delta = 0 
    RETURN 0

    IF @bravo IS NULL OR @bravo <= 0
        RETURN 100

    IF (@charle + @delta) / @bravo <= 0
        RETURN 100
    DECLARE @x int = DATEDIFF(dd, GETDATE(),'1/1/2000')     
    RETURN @alpha * POWER((100 / @delta), (-2 * POWER(@charle * @bravo, @x/365)))
END
GO

-- multi-statement table valued function 
CREATE FUNCTION dbo.Compute_value_mtvf
(
  @alpha FLOAT,
  @bravo FLOAT,
  @charle FLOAT,
  @delta FLOAT
)
RETURNS  @sometable TABLE (newValue float) AS 
    BEGIN
    INSERT @sometable VALUES
(
  CASE WHEN @alpha IS NULL OR @alpha = 0 OR @delta IS NULL OR @delta = 0 THEN 0
       WHEN @bravo IS NULL OR @bravo <= 0 THEN 100
       ELSE @alpha * POWER((100 / @delta), 
             (-2 * POWER(@charle * @bravo, DATEDIFF(DAY,GETDATE(),'1/1/2000')/365)))
  END
)
RETURN;
END
GO

-- INLINE table valued function
CREATE FUNCTION dbo.Compute_value_itvf
(
  @alpha FLOAT,
  @bravo FLOAT,
  @charle FLOAT,
  @delta FLOAT
)
RETURNS TABLE WITH SCHEMABINDING AS RETURN
SELECT newValue = 
  CASE WHEN @alpha IS NULL OR @alpha = 0 OR @delta IS NULL OR @delta = 0 THEN 0
       WHEN @bravo IS NULL OR @bravo <= 0 THEN 100
       ELSE @alpha * POWER((100 / @delta), 
             (-2 * POWER(@charle * @bravo, DATEDIFF(DAY,GETDATE(),'1/1/2000')/365)))
  END
GO

Sekarang untuk beberapa contoh data dan uji kinerja:

SET NOCOUNT ON;
CREATE TABLE #someTable (alpha FLOAT, bravo FLOAT, charle FLOAT, delta FLOAT);
INSERT #someTable
SELECT TOP (100000)
  abs(checksum(newid())%10)+1, abs(checksum(newid())%10)+1, 
  abs(checksum(newid())%10)+1, abs(checksum(newid())%10)+1
FROM sys.all_columns a, sys.all_columns b;

PRINT char(10)+char(13)+'scalar'+char(10)+char(13)+replicate('-',60);
GO
DECLARE @st datetime = getdate(), @z float;

SELECT @z = dbo.Compute_value_scalar(t.alpha, t.bravo, t.charle, t.delta)
FROM #someTable t;

PRINT DATEDIFF(ms, @st, getdate());
GO

PRINT char(10)+char(13)+'mtvf'+char(10)+char(13)+replicate('-',60);
GO
DECLARE @st datetime = getdate(), @z float;

SELECT @z = f.newValue
FROM #someTable t
CROSS APPLY dbo.Compute_value_mtvf(t.alpha, t.bravo, t.charle, t.delta) f;

PRINT DATEDIFF(ms, @st, getdate());
GO

PRINT char(10)+char(13)+'itvf'+char(10)+char(13)+replicate('-',60);
GO
DECLARE @st datetime = getdate(), @z float;

SELECT @z = f.newValue
FROM #someTable t
CROSS APPLY dbo.Compute_value_itvf(t.alpha, t.bravo, t.charle, t.delta) f;

PRINT DATEDIFF(ms, @st, getdate());
GO

Hasil:

scalar
------------------------------------------------------------
2786

mTVF
------------------------------------------------------------
41536

iTVF
------------------------------------------------------------
153

Udf skalar berjalan selama 2,7 detik, 41 detik untuk mtvf dan 0,153 detik untuk iTVF. Untuk memahami alasannya, mari kita lihat perkiraan rencana eksekusi:

Anda tidak melihat ini saat melihat rencana eksekusi yang sebenarnya tetapi, dengan skalar udf dan mtvf, pengoptimal memanggil beberapa subrutin yang dieksekusi dengan buruk untuk setiap baris; iTVF tidak. Mengutip Perubahan karir Paul White artikel tentang BERLAKU Paulus menulis:

Dengan kata lain, iTVF memungkinkan pengoptimal untuk mengoptimalkan kueri dengan cara yang tidak mungkin dilakukan ketika semua kode lain perlu dieksekusi. Salah satu dari banyak contoh lain mengapa iTVF lebih unggul adalah mereka adalah satu-satunya dari tiga jenis fungsi yang disebutkan di atas yang memungkinkan paralelisme. Mari kita jalankan setiap fungsi sekali lagi, kali ini dengan mengaktifkan rencana Eksekusi Aktual dan dengan traceflag 8649 (yang memaksa rencana eksekusi paralel):

-- don't need so many rows for this test
TRUNCATE TABLE #sometable;
INSERT #someTable 
SELECT TOP (10)
  abs(checksum(newid())%10)+1, abs(checksum(newid())%10)+1, 
  abs(checksum(newid())%10)+1, abs(checksum(newid())%10)+1
FROM sys.all_columns a;

DECLARE @x float;

SELECT TOP (10) @x = dbo.Compute_value_scalar(t.alpha, t.bravo, t.charle, t.delta)
FROM #someTable t
ORDER BY dbo.Compute_value_scalar(t.alpha, t.bravo, t.charle, t.delta)
OPTION (QUERYTRACEON 8649);

SELECT TOP (10)  @x = f.newValue
FROM #someTable t
CROSS APPLY dbo.Compute_value_mtvf(t.alpha, t.bravo, t.charle, t.delta) f
ORDER BY f.newValue
OPTION (QUERYTRACEON 8649);

SELECT @x = f.newValue
FROM #someTable t
CROSS APPLY dbo.Compute_value_itvf(t.alpha, t.bravo, t.charle, t.delta) f
ORDER BY f.newValue
OPTION (QUERYTRACEON 8649);

Rencana eksekusi:

Panah yang Anda lihat untuk rencana eksekusi iTVF adalah paralelisme - semua CPU Anda (atau sebanyak MAXDOP instance SQL Anda) pengaturan memungkinkan) bekerja sama. Skalar T-SQL dan mtvf UDF tidak dapat melakukan itu. Ketika Microsoft memperkenalkan UDF skalar sebaris maka saya akan menyarankan itu untuk apa yang Anda lakukan tetapi, sampai saat itu:jika kinerja adalah yang Anda cari maka inline adalah satu-satunya cara untuk pergi dan, untuk itu, iTVF adalah satu-satunya permainan di kota.

Perhatikan bahwa saya terus menekankan T-SQL ketika berbicara tentang fungsi... Fungsi bernilai Skalar dan Tabel CLR bisa jadi baik-baik saja, tetapi itu topik yang berbeda.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Menggunakan SQL Server sebagai penyimpanan Gambar

  2. Instalasi SQL Server 2017

  3. Cara Menghubungkan ke Beberapa Server SQL dalam Satu Klik (Grup Server Terdaftar) - Tutorial SQL Server / TSQL Bagian 5

  4. Kinerja lambat dari SqlDataReader

  5. Konversi string ke UPPERCASE di SQL Server