3

我有几个使用外部应用的存储过程。外部应用内的查询总是相同的,所以我可以构建一个通用的表值函数,这给我代码重用带来了明显的好处,但我想知道这两种方式是否会对性能产生影响。如果我调用一个函数,我会受到打击吗?

例如:

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
4

2 回答 2

4

这取决于函数类型:

  1. 如果函数是一个内联表值函数,那么这个函数将被认为是一个“参数化”视图并且SQL Server可以做一些优化工作。

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

对于我使用的下一个测试AdventureWorks2008(您可以从 CodePlex 下载此数据库)。在此示例数据库中,您可能会找到一个inline table-valued function名为[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

这是来自的输出SQL Profiler在此处输入图像描述

结论:您可以看到,使用查询或内联表值函数OUTER APPLY将为您提供相同的性能(逻辑读取)。另外:多步表值函数(通常)更昂贵

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

--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.
于 2012-05-15T20:06:21.410 回答
2

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

原因

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

检查Apply和Join之间的区别

于 2012-05-15T16:16:30.983 回答