Sqlserver
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Sqlserver

Gói dựa trên đặt chạy chậm hơn so với hàm có giá trị vô hướng với nhiều điều kiện

Thuật ngữ từ khóa ở đây là INLINE CÁC CHỨC NĂNG CÓ GIÁ TRỊ CỦA BẢNG . Bạn có hai loại hàm có giá trị theo bảng T-SQL:đa câu lệnh và nội dòng. Nếu hàm T-SQL của bạn bắt đầu bằng câu lệnh BEGIN thì nó sẽ là một câu lệnh vô hướng - vô hướng hoặc theo cách khác. Bạn không thể đưa bảng tạm vào một nội tuyến hàm có giá trị bảng, vì vậy tôi giả sử bạn đã chuyển từ hàm vô hướng sang hàm có giá trị bảng câu lệnh mutli có thể sẽ tệ hơn.

Hàm có giá trị trong bảng nội tuyến (iTVF) của bạn sẽ trông giống như sau:

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;

Lưu ý rằng, trong mã bạn đã đăng, DATEDIFF của bạn câu lệnh thiếu datepart tham số. Nếu nên trông giống như sau:

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

Đi xa hơn một chút - điều quan trọng là phải hiểu tại sao iTVF lại tốt hơn các hàm có giá trị vô hướng của T-SQL do người dùng xác định. Không phải vì các hàm có giá trị bảng nhanh hơn các hàm có giá trị vô hướng, mà là do việc triển khai các hàm nội tuyến T-SQL của Microsoft nhanh hơn việc triển khai các hàm T-SQL không nội tuyến của họ. Lưu ý ba chức năng sau đây thực hiện tương tự:

-- 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

Bây giờ để biết một số dữ liệu mẫu và kiểm tra hiệu suất:

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

Kết quả:

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

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

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

Udf vô hướng chạy trong 2,7 giây, 41 giây cho mtvf và 0,153 giây cho iTVF. Để hiểu lý do tại sao chúng ta hãy xem xét các kế hoạch thực hiện ước tính:

Bạn không thấy điều này khi nhìn vào kế hoạch thực thi thực tế, nhưng với udf và mtvf vô hướng, trình tối ưu hóa gọi một số chương trình con được thực thi kém cho mỗi hàng; iTVF thì không. Trích dẫn Paul White thay đổi nghề nghiệp bài viết về ĐĂNG KÝ Paul viết:

Nói cách khác, iTVF's enable to Optimizer để tối ưu hóa truy vấn theo những cách không thể thực hiện được khi tất cả các mã khác cần được thực thi. Một trong nhiều ví dụ khác về lý do tại sao iTVF vượt trội hơn là chúng là một trong ba loại chức năng nói trên cho phép thực hiện song song. Hãy chạy từng chức năng một lần nữa, lần này với kế hoạch Thực thi thực tế được bật và với traceflag 8649 (buộc một kế hoạch thực thi song song):

-- 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);

Kế hoạch thực hiện:

Các mũi tên mà bạn thấy cho kế hoạch thực thi của iTVF là song song - tất cả các mũi tên của CPU của bạn (hoặc nhiều như MAXDOP của phiên bản SQL của bạn cài đặt cho phép) làm việc cùng nhau. T-SQL vô hướng và mtvf UDF không thể làm điều đó. Khi Microsoft giới thiệu UDF vô hướng nội tuyến thì tôi sẽ đề xuất những UDF đó cho những gì bạn đang làm, nhưng cho đến lúc đó:nếu hiệu suất là những gì bạn đang tìm kiếm thì nội tuyến là cách duy nhất để đi và đối với điều đó, iTVF là trò chơi duy nhất trong thị trấn.

Lưu ý rằng tôi đã liên tục nhấn mạnh T-SQL khi nói về các hàm ... Các hàm có giá trị CLR Scalar và Table có thể tốt nhưng đó là một chủ đề khác.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Loại bỏ các dấu cách theo dõi và cập nhật trong các cột trong SQL Server

  2. Các chức năng SQL Server đơn giản để giải quyết các vấn đề trong thế giới thực

  3. Hàm SQL - giai thừa

  4. Sao chép kết quả truy vấn SQL Server vào bảng Access 2010

  5. Làm cách nào để ngăn quy mô DateTimeOffset gây ra một ChangeConflictException trong linq thành Sql?