Navicat 博客

了解 SQL Server 的 CROSS APPLY 和 OUTER APPLY 查询 - 第 2 部分 2021 年 10 月 19 日,由 Robert Gravelle 撰写

CROSS APPLY 和 OUTER APPLY 示例

上一篇文章介绍了 APPLY 运算符并介绍了它与常规 JOIN 的区别。在今天的后续文章中,我们将比较 APPLY 与 INNER JOIN 的性能,并学习如何将 APPLY 与表值函数一起使用。

APPLY 和 INNER JOIN 比较

回顾一下,在第 1 部分的末尾,我们运行了一个由两部分组成的查询:第一个查询从 Department 表中选择数据,并使用 CROSS APPLY 为 Department 表的每条记录对 Employee 表求值;第二个查询将 Department 表与 Employee 表联接起来以产生相同的结果:

CROSS APPLY vs INNER JOIN (88K)

Navicat 中,我们可以点击“解释”按钮来获取有关数据库执行计划的有价值的信息。以下是关于上述查询的执行计划:

explain_ex_1 (176K)

尽管两个查询的执行计划相似且成本相同,但它们的执行计划确实有所不同:

  • APPLY 查询使用计算标量。它是一个运算符,用于通过执行产生计算值的标量计算操作从现有行值计算新值。这些标量计算包括标量值的转换或串联。请注意,计算标量运算符不是一个耗费大量资源的运算符,并且对我们查询的整体增加的成本非常小,能导致最小的开销。
  • JOIN 查询包含附加的聚集索引扫描。当 SQL Server 从上到下读取聚集索引中的行时(例如在非键列中搜索数据时),就会发生这种情况。这是一个比计算标量稍微成本高一些的操作。

使用 APPLY 运算符联接表值函数和表

表值函数是用户定义的函数,它返回表类型的数据。表值函数的返回类型是表,因此,你可以像使用表一样使用表值函数。将表值函数与其他表联接是 APPLY 运算符的设计目的。

让我们创建一个表值函数,它接受一个 DepartmentID 作为其参数并返回属于该部门的所有员工。在 Navicat 中,我们可以通过点击主工具栏上的大函数按钮,然后点击功能工具栏上的新建函数来创建一个函数:

function_button (22K)

这是单击“保存”按钮后的 GetAllEmployeesForDepartment 函数:

GetAllEmployeesForDepartment_function (43K)

看看当我们使用 CROSS OUTER 和 OUTER APPLY 将我们的新函数加入每个部门时会发生什么:

cross_apply_vs_outer_apply (94K)

在每种情况下,查询都会传递来自外部表表达式的每一行的 DepartmentID,并为每一行评估函数,类似于一个相关子查询。CROSS APPLY 仅返回相关数据,而 OUTER APPLY 也会返回非相关数据,这导致缺失列会显示 NULL。

我们无法用 INNER JOIN/LEFT OUTER JOIN 替换上述查询中的 CROSS/OUTER APPLY。这样做会产生错误“The multi-part identifier "D.DepartmentID" could not be bound.”。这是因为外部(JOINed)查询的执行上下文与函数(或派生表)的执行上下文不同。因而,你不能将外部查询中的值或变量作为参数绑定到函数。因此,此类查询需要 APPLY 运算符。

总结

我们对 CROSS APPLY 和 OUTER APPLY 语句的研究到此结束。总而言之,你在查询中必须使用表值函数时,就需要使用 APPLY 运算符,但它也可以用于内联 SELECT 语句。

Navicat 文章
频道条目
分享
文章归档