Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/27.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 多语句表值函数与内联表值函数_Sql_Sql Server_Sql Server 2008_Tsql_User Defined Functions - Fatal编程技术网

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

Sql 多语句表值函数与内联表值函数,sql,sql-server,sql-server-2008,tsql,user-defined-functions,Sql,Sql Server,Sql Server 2008,Tsql,User Defined Functions,举几个例子,以防万一: 内联表值 多语句表值 使用一种类型(行内或多语句)比使用另一种类型有优势吗?当一个比另一个更好时,是否存在某些场景,或者这些差异纯粹是句法上的?我意识到这两个示例查询所做的事情是不同的,但有什么理由我会这样写吗 阅读有关它们的文章,并没有真正解释它们的优点/区别。我认为,你的例子很好地回答了这个问题。第一个函数可以作为单个选择来完成,这是使用内联样式的一个很好的理由。第二个可能作为单个语句完成(使用子查询获取最大日期),但是一些编码人员可能会发现,像您这样在多个语句中执行

举几个例子,以防万一:

内联表值

多语句表值

使用一种类型(行内或多语句)比使用另一种类型有优势吗?当一个比另一个更好时,是否存在某些场景,或者这些差异纯粹是句法上的?我意识到这两个示例查询所做的事情是不同的,但有什么理由我会这样写吗


阅读有关它们的文章,并没有真正解释它们的优点/区别。

我认为,你的例子很好地回答了这个问题。第一个函数可以作为单个选择来完成,这是使用内联样式的一个很好的理由。第二个可能作为单个语句完成(使用子查询获取最大日期),但是一些编码人员可能会发现,像您这样在多个语句中执行该操作更容易阅读或更自然。有些函数无法在一条语句中完成,因此需要多语句版本


我建议尽可能使用最简单的(内联)语句,必要时(显然)或个人偏好/可读性需要额外键入时使用多语句。

如果要执行查询,可以加入内联表值函数,如:

SELECT
    a.*,b.*
    FROM AAAA a
        INNER JOIN MyNS.GetUnshippedOrders() b ON a.z=b.z
它将产生很少的开销,运行良好

如果尝试在类似查询中使用多语句表值,则会出现性能问题:

SELECT
    x.a,x.b,x.c,(SELECT OrderQty FROM MyNS.GetLastShipped(x.CustomerID)) AS Qty
    FROM xxxx   x

因为您将为返回的每一行执行函数1次,所以随着结果集变大,它的运行速度将越来越慢。

在研究Matt的评论时,我修改了我的原始陈述。他是正确的,即使内联表值函数(ITVF)和多语句表值函数(MSTVF)都只是执行SELECT语句,它们的性能也会有所不同。SQL Server将ITVF视为一个
视图
,因为它将使用有关表的最新统计信息计算执行计划。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
在查询中,优化器将能够调用该函数一次并构建更好的执行计划,但它仍然不会比等效的非参数化ITV或
视图更好

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

多亏了马特

添加

由于我最近看到了这一点,这里有一个由Wayne Sheffield完成的优秀分析,比较了内联表值函数和多语句函数之间的性能差异


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

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

对于多语句表值函数,将为函数本身创建执行计划并存储在执行计划缓存中(函数第一次执行后)。如果多语句表值函数用作较大查询的一部分,则优化程序不知道函数返回的内容,因此做出一些标准假设-实际上,它假设函数将返回一行,函数的返回将通过对一个单行表进行表扫描来访问

当多语句表值函数返回大量行并在外部查询中与之连接时,它们的性能可能会很差。性能问题主要取决于这样一个事实,即优化者将在假设返回一行的情况下生成一个计划,这不一定是最合适的计划


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

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

查看您可以找到良好的描述和性能基准。

我没有测试过这一点,但多语句函数会缓存结果集。在某些情况下,优化器可能会进行太多的工作,无法内联函数。例如,假设您有一个函数,该函数根据传递的“公司编号”返回来自不同数据库的结果
SELECT
    x.a,x.b,x.c,(SELECT OrderQty FROM MyNS.GetLastShipped(x.CustomerID)) AS Qty
    FROM xxxx   x
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