Es hängt vom Funktionstyp ab:
-
Wenn die Funktion eine Inline-Tabellenwertfunktion ist, wird diese Funktion als "parametrisierte" Ansicht und
SQL Server
betrachtet kann etwas Optimierungsarbeit leisten. -
Wenn es sich bei der Funktion um eine mehrstufige Tabellenwertfunktion handelt, ist dies für
SQL Server
schwierig um die Anweisung und die Ausgabe vonSET STATISTICS IO
zu optimieren wird irreführend sein.
Für den nächsten Test habe ich AdventureWorks2008
verwendet (Sie können diese Datenbank von CodePlex herunterladen). In dieser Beispieldatenbank finden Sie möglicherweise eine inline table-valued function
mit dem Namen [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
Ich habe eine neue Funktion namens [Sales].[ufnGetCheapestProductMultiStep]
erstellt . Diese Funktion ist eine 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
Jetzt können wir die nächsten Tests durchführen:
--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
Und dies ist die Ausgabe von SQL Profiler
:
Fazit :Sie können dies mit einer Abfrage oder einer Inline-Tabellenwertfunktion mit OUTER APPLY
sehen erhalten Sie die gleiche Leistung (logische Lesevorgänge). Plus:die mehrstufigen Tabellenwertfunktionen sind (normalerweise) teurer
.
Hinweis :Ich empfehle die Verwendung von SET STATISTICS IO
nicht um den IO
zu messen für skalare und mehrstufige Tabellenwertfunktionen, da die Ergebnisse falsch sein können. Beispielsweise für diese Tests die Ausgabe von SET STATISTICS IO ON
wird sein:
--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.