多语句表值函数与内联表值函数

And*_*ewC 188 sql t-sql sql-server user-defined-functions sql-server-2008

举几个例子,只是说:

内联表值

CREATE FUNCTION MyNS.GetUnshippedOrders()
RETURNS TABLE
AS 
RETURN SELECT a.SaleId, a.CustomerID, b.Qty
    FROM Sales.Sales a INNER JOIN Sales.SaleDetail b
        ON a.SaleId = b.SaleId
        INNER JOIN Production.Product c ON b.ProductID = c.ProductID
    WHERE a.ShipDate IS NULL
GO
Run Code Online (Sandbox Code Playgroud)

多语句表值

CREATE FUNCTION MyNS.GetLastShipped(@CustomerID INT)
RETURNS @CustomerOrder TABLE
(SaleOrderID    INT         NOT NULL,
CustomerID      INT         NOT NULL,
OrderDate       DATETIME    NOT NULL,
OrderQty        INT         NOT NULL)
AS
BEGIN
    DECLARE @MaxDate DATETIME

    SELECT @MaxDate = MAX(OrderDate)
    FROM Sales.SalesOrderHeader
    WHERE CustomerID = @CustomerID

    INSERT @CustomerOrder
    SELECT a.SalesOrderID, a.CustomerID, a.OrderDate, b.OrderQty
    FROM Sales.SalesOrderHeader a INNER JOIN Sales.SalesOrderHeader b
        ON a.SalesOrderID = b.SalesOrderID
        INNER JOIN Production.Product c ON b.ProductID = c.ProductID
    WHERE a.OrderDate = @MaxDate
        AND a.CustomerID = @CustomerID
    RETURN
END
GO
Run Code Online (Sandbox Code Playgroud)

使用一种类型(在线或多语句)优于另一种类型是否有优势?当一个人比另一个人好或者纯粹是语法上的差异时,是否存在某些情况?我意识到这两个示例查询正在做不同的事情,但我有理由以这种方式编写它们吗?

阅读它们以及优点/差异尚未得到解释.

Tho*_*mas 136

在研究马特的评论时,我修改了我原来的陈述.他是正确的,即使它们都只是执行SELECT语句,内联表值函数(ITVF)和多语句表值函数(MSTVF)之间的性能也会有所不同.SQL Server会将ITVF视为有点像VIEW,因为它将使用有关表的最新统计信息来计算执行计划.MSTVF等效于将SELECT语句的全部内容填充到表变量中,然后加入到该变量中.因此,编译器不能对MSTVF中的表使用任何表统计信息.因此,在所有条件相同的情况下(他们很少),ITVF将比MSTVF表现更好.在我的测试中,完成时间的性能差异可以忽略不计,但从统计角度来看,这是显而易见的.

在您的情况下,这两个函数在功能上并不相同.MSTV函数每次调用时都会执行额外的查询,最重要的是,会对客户ID进行过滤.在大型查询中,优化器将无法利用其他类型的连接,因为它需要为每个传递的customerId调用该函数.但是,如果您重新编写MSTV函数,如下所示:

CREATE FUNCTION MyNS.GetLastShipped()
RETURNS @CustomerOrder TABLE
    (
    SaleOrderID    INT         NOT NULL,
    CustomerID      INT         NOT NULL,
    OrderDate       DATETIME    NOT NULL,
    OrderQty        INT         NOT NULL
    )
AS
BEGIN
    INSERT @CustomerOrder
    SELECT a.SalesOrderID, a.CustomerID, a.OrderDate, b.OrderQty
    FROM Sales.SalesOrderHeader a 
        INNER JOIN Sales.SalesOrderHeader b
            ON a.SalesOrderID = b.SalesOrderID
        INNER JOIN Production.Product c 
            ON b.ProductID = c.ProductID
    WHERE a.OrderDate = (
                        Select Max(SH1.OrderDate)
                        FROM Sales.SalesOrderHeader As SH1
                        WHERE SH1.CustomerID = A.CustomerId
                        )
    RETURN
END
GO
Run Code Online (Sandbox Code Playgroud)

在查询中,优化器将能够调用该函数一次并构建更好的执行计划,但它仍然不会比等效的,非参数化的ITVS或者更好VIEW.

在可行时,ITVF应优先于MSTVF,因为表中列的数据类型,可空性和排序规则,而您在多语句表值函数中声明这些属性,重要的是,您将从ITVF获得更好的执行计划.根据我的经验,我没有发现很多情况下ITVF比VIEW更好,但里程可能会有所不同.

感谢马特.

加成

自从我最近看到这个问题以来,Wayne Sheffield就是一个很好的分析,比较了内联表值函数和多语句函数之间的性能差异.

他原来的博文.

在SQL Server Central上复制

  • 这根本不是真的 - 多语句函数通常是一个巨大的性能损失,因为它们阻止查询优化器使用统计信息.如果我每次看到多语句功能使用1美元导致执行计划的选择非常差(主要是因为它通常估计返回的行数为1),我就足够买一辆小型车了. (40认同)

Pau*_*lin 27

在内部,SQL Server将内联表值函数视为视图,并将多语句表值函数视为与存储过程类似.

当内联表值函数用作外部查询的一部分时,查询处理器将扩展UDF定义并使用这些对象上的索引生成访问基础对象的执行计划.

对于多语句表值函数,为函数本身创建执行计划并将其存储在执行计划缓存中(一旦第一次执行该函数).如果多语句表值函数被用作较大查询的一部分,则优化器不知道函数返回什么,因此做出一些标准假设 - 实际上它假设函数将返回单行,并且返回通过对具有单行的表使用表扫描来访问该函数.

多语句表值函数表现不佳的情况是,它们返回大量行并在外部查询中连接.性能问题主要取决于优化器将生成一个假定返回单行的计划,这不一定是最合适的计划.

作为一般经验法则,我们发现,由于这些潜在的性能问题,应该使用内联表值函数而不是多语句函数(当UDF将用作外部查询的一部分时).

  • 除非您需要在另一个查询中加入这些结果. (6认同)
  • 虽然它可以处理类似于存储过程的多语句表值函数,但是功能相同的存储过程比大数据集的表值函数快得多.我坚持使用多语句表值函数存储过程. (2认同)

小智 13

还有另一个不同之处.可以插入,更新和删除内联表值函数 - 就像视图一样.类似的限制适用 - 无法使用聚合更新函数,无法更新计算列等.