外部性能适用功能

时间:2012-05-15 14:37:42

标签: sql-server sql-server-2008

我有几个使用外部应用的存储过程。外部apply中的查询总是相同的,所以我可以构建一个公用表值函数,这给了我重复使用代码的明显好处,但是我想知道是否存在性能影响。如果我打电话给某个功能,我会受到打击吗?

例如:

SELECT
    m.[ID],
    m.[MyField],
    o.[OtherField]
FROM
    [MyTable] m
OUTER Apply
(
    fMyFunction(m.[ID])
)

VS

SELECT
    mt.[ID],
    mt.[MyField],
    o.[OtherField]
FROM
    [MyTable] mt
OUTER Apply
(
    SELECT TOP 1
        ot.[OtherField]
    FROM
        [OtherTable] ot 
    WHERE
        ot.[ID] = m.[ID]
) o

2 个答案:

答案 0 :(得分:3)

取决于功能类型:

1)如果函数是内联表值函数,则该函数将被视为“参数化”视图,SQL Server可以进行一些优化工作。

2)如果函数是多步表值函数,则SQL Server很难优化语句,SET STATISTICS IO的输出会产生误导。

对于下一个测试,我使用了AdventureWorks2008(您可以从CodePlex下载此数据库)。在此示例数据库中,您可能会找到名为inline table-valued function的{​​{1}}:

[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 的新函数。此功能是[Sales].[ufnGetCheapestProductMultiStep]

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

这是--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 的输出: enter image description here

结论:您可以看到使用带有SQL Profiler的查询或内联表值函数将为您提供相同的性能(逻辑读取)。另外:the multi-step table-valued functions are (usually) more expensive

注意:我不建议使用OUTER APPLY来衡量标量和多步表值函数的SET STATISTICS IO,因为结果可能有误。例如,对于这些测试,IO的输出将为:

SET STATISTICS IO ON

表'产品'。扫描计数1,逻辑读取5,物理读取0,预读读取0,lob逻辑读取0,lob物理读取0,lob预读读取0。

答案 1 :(得分:1)

Outer Apply不应该在这里考虑......

<强>原因

它将迭代MyTable的每个记录,并将搜索Outer Apply表中的相应记录,尽管您将从MyTable获取所有记录。所以它应该用Join(Left / Inner)代替。这将加快查询速度,尤其是当您要获取大量记录时。

<强> Check the difference between Apply and Join