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.