Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Leistung der äußeren Anwendung mit Funktion

Es hängt vom Funktionstyp ab:

  1. Wenn die Funktion eine Inline-Tabellenwertfunktion ist, wird diese Funktion als "parametrisierte" Ansicht und SQL Server betrachtet kann etwas Optimierungsarbeit leisten.

  2. Wenn es sich bei der Funktion um eine mehrstufige Tabellenwertfunktion handelt, ist dies für SQL Server schwierig um die Anweisung und die Ausgabe von SET 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.