Nó phụ thuộc vào loại chức năng:
-
Nếu hàm là một hàm có giá trị bảng nội tuyến thì hàm này sẽ được coi là một dạng xem "được tham số hóa" và
SQL Server
có thể thực hiện một số công việc tối ưu hóa. -
Nếu hàm là hàm có giá trị bảng nhiều bước thì sẽ khó đối với
SQL Server
để tối ưu hóa câu lệnh và kết quả từSET STATISTICS IO
sẽ gây hiểu lầm.
Đối với thử nghiệm tiếp theo, tôi đã sử dụng AdventureWorks2008
(bạn có thể tải xuống cơ sở dữ liệu này từ CodePlex). Trong cơ sở dữ liệu mẫu này, bạn có thể tìm thấy một inline table-valued function
được đặt tên là [Sales].[ufnGetCheapestProduct]
:
ALTER FUNCTION [Sales].[ufnGetCheapestProduct](@ProductID INT)
RETURNS TABLE
AS
RETURN
SELECT dt.ProductID
,dt.UnitPrice
FROM
(
SELECT d.SalesOrderDetailID
,d.UnitPrice
,d.ProductID
,ROW_NUMBER() OVER(PARTITION BY d.ProductID ORDER BY d.UnitPrice ASC, d.SalesOrderDetailID) RowNumber
FROM Sales.SalesOrderDetail d
WHERE d.ProductID = @ProductID
) dt
WHERE dt.RowNumber = 1
Tôi đã tạo một chức năng mới có tên [Sales].[ufnGetCheapestProductMultiStep]
. Hàm này là một multi-step table-valued function
:
CREATE FUNCTION [Sales].[ufnGetCheapestProductMultiStep](@ProductID INT)
RETURNS @Results TABLE (ProductID INT PRIMARY KEY, UnitPrice MONEY NOT NULL)
AS
BEGIN
INSERT @Results(ProductID, UnitPrice)
SELECT dt.ProductID
,dt.UnitPrice
FROM
(
SELECT d.SalesOrderDetailID
,d.UnitPrice
,d.ProductID
,ROW_NUMBER() OVER(PARTITION BY d.ProductID ORDER BY d.UnitPrice ASC, d.SalesOrderDetailID) RowNumber
FROM Sales.SalesOrderDetail d
WHERE d.ProductID = @ProductID
) dt
WHERE dt.RowNumber = 1;
RETURN;
END
Bây giờ, chúng tôi có thể chạy các thử nghiệm tiếp theo:
--Test 1
SELECT p.ProductID, p.Name, oa1.*
FROM Production.Product p
OUTER APPLY
(
SELECT dt.ProductID
,dt.UnitPrice
FROM
(
SELECT d.SalesOrderDetailID
,d.UnitPrice
,d.ProductID
,ROW_NUMBER() OVER(PARTITION BY d.ProductID ORDER BY d.UnitPrice ASC, d.SalesOrderDetailID) RowNumber
FROM Sales.SalesOrderDetail d
WHERE d.ProductID = p.ProductID
) dt
WHERE dt.RowNumber = 1
) oa1
--Test 2
SELECT p.ProductID, p.Name, oa2.*
FROM Production.Product p
OUTER APPLY [Sales].[ufnGetCheapestProduct](p.ProductID) oa2
--Test 3
SELECT p.ProductID, p.Name, oa3.*
FROM Production.Product p
OUTER APPLY [Sales].[ufnGetCheapestProductMultiStep](p.ProductID) oa3
Và đây là kết quả từ SQL Profiler
:
Kết luận :bạn có thể thấy rằng bằng cách sử dụng truy vấn hoặc hàm có giá trị bảng nội tuyến với OUTER APPLY
sẽ cung cấp cho bạn cùng một hiệu suất (đọc hợp lý). Thêm vào đó: các hàm có giá trị bảng nhiều bước (thường) đắt hơn
.
Lưu ý :Tôi không khuyên bạn nên sử dụng SET STATISTICS IO
để đo lường IO
cho các hàm có giá trị bảng vô hướng và nhiều bước vì kết quả có thể sai. Ví dụ:đối với các bài kiểm tra này, kết quả đầu ra từ SET STATISTICS IO ON
sẽ là:
--Test 1
Table 'SalesOrderDetail'. Scan count 504, logical reads 1513, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Product'. Scan count 1, logical reads 5, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
--Test 2
Table 'SalesOrderDetail'. Scan count 504, logical reads 1513, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Product'. Scan count 1, logical reads 5, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
--Test 3
Table '#064EAD61'. Scan count 504, logical reads 1008 /*WRONG*/, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Product'. Scan count 1, logical reads 5, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.