Záleží na typu funkce:
-
Pokud je funkce inline tabulka s hodnotou, bude tato funkce považována za "parametrizovaný" pohled a
SQL Server
může provést nějakou optimalizační práci. -
Pokud je funkce vícekroková tabulková funkce, pak je pro
SQL Server
obtížná k optimalizaci příkazu a výstupu zSET STATISTICS IO
bude zavádějící.
Pro další test jsem použil AdventureWorks2008
(tuto databázi si můžete stáhnout z CodePlex). V této ukázkové databázi můžete najít inline table-valued function
s názvem [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
Vytvořil jsem novou funkci s názvem [Sales].[ufnGetCheapestProductMultiStep]
. Tato funkce je 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
Nyní můžeme spustit další testy:
--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
A toto je výstup z SQL Profiler
:
Závěr :můžete vidět, že pomocí dotazu nebo funkce vložené tabulky s hodnotou OUTER APPLY
vám poskytne stejný výkon (logické čtení). Plus:funkce s vícekrokovými tabulkami jsou (obvykle) dražší
.
Poznámka :Nedoporučuji používat SET STATISTICS IO
pro měření IO
pro skalární a vícekrokové tabulky hodnotné funkce, protože výsledky mohou být chybné. Například pro tyto testy výstup z SET STATISTICS IO ON
bude:
--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.