Dipende dal tipo di funzione:
-
Se la funzione è una funzione inline con valori di tabella, questa funzione sarà considerata una vista "parametrizzata" e
SQL Server
può fare un po' di lavoro di ottimizzazione. -
Se la funzione è una funzione con valori di tabella a più passaggi, è difficile per
SQL Server
per ottimizzare l'istruzione e l'output daSET STATISTICS IO
sarà fuorviante.
Per il prossimo test ho usato AdventureWorks2008
(puoi scaricare questo database da CodePlex). In questo database di esempio puoi trovare una inline table-valued function
denominato [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
Ho creato una nuova funzione denominata [Sales].[ufnGetCheapestProductMultiStep]
. Questa funzione è una 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
Ora possiamo eseguire i prossimi test:
--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
E questo è l'output di SQL Profiler
:
Conclusione :puoi vederlo usando una query o una funzione inline con valori di tabella con OUTER APPLY
ti darà le stesse prestazioni (letture logiche). Inoltre:le funzioni multi-step con valori di tabella sono (di solito) più costose
.
Nota :Non consiglio di usare SET STATISTICS IO
per misurare il IO
per funzioni scalari e multi-step con valori di tabella perché i risultati possono essere errati. Ad esempio, per questi test l'output da SET STATISTICS IO ON
sarà:
--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.