sql >> Databáze >  >> RDS >> Sqlserver

Výkon vnější aplikace s funkcí

Záleží na typu funkce:

  1. 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.

  2. Pokud je funkce vícekroková tabulková funkce, pak je pro SQL Server obtížná k optimalizaci příkazu a výstupu z SET 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.


  1. Seskupené souhrnné Pushdown

  2. Jak připojit Oracle Cloud Instance pomocí Oracle Cloud Shell?

  3. Ořízněte mezery (nový řádek a místo tabulátoru) v řetězci v Oracle

  4. Oracle SYS_REFCURSOR nelze použít jako návratový typ