Зависи от типа функция:
-
Ако функцията е вградена функция с таблична стойност, тогава тази функция ще се счита за "параметризиран" изглед и
SQL Server
може да извърши известна оптимизационна работа. -
Ако функцията е многоетапна функция с таблична стойност, това е трудно за
SQL Server
за оптимизиране на оператора и изхода отSET STATISTICS IO
ще бъдат подвеждащи.
За следващия тест използвах AdventureWorks2008
(можете да изтеглите тази база данни от CodePlex). В тази примерна база данни може да намерите inline table-valued function
с име [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
Създадох нова функция с име [Sales].[ufnGetCheapestProductMultiStep]
. Тази функция е 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
Сега можем да изпълним следващите тестове:
--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
И това е резултатът от SQL Profiler
:
Заключение :можете да видите това с помощта на заявка или вградена функция с таблична стойност с OUTER APPLY
ще ви даде същата производителност (логически четения). Плюс:функциите с таблични стойности с много стъпки (обикновено) са по-скъпи
.
Забележка :Не препоръчвам да използвате SET STATISTICS IO
за измерване на IO
за скаларни и многостъпкови таблично означени функции, тъй като резултатите могат да бъдат грешни. Например, за тези тестове изходът от SET STATISTICS IO ON
ще бъде:
--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.