SQL Server CROSS APPLY 与 OUTER APPLY 的性能与适用场景对比

7次阅读

该用 CROSS appLY 而不是 JOIN 时:右侧表达式需依赖左侧行值(如 TVF 参数为左表字段)、返回多行或零行,且无法被普通 JOIN 表达;OUTER APPLY 则适用于需保留左表行且右侧逻辑逐行执行的场景。

SQL Server CROSS APPLY 与 OUTER APPLY 的性能与适用场景对比

什么时候该用 CROSS APPLY 而不是 JOIN

CROSS APPLY 的核心价值不是“替代 INNER JOIN”,而是支持右侧表达式依赖左侧行值,且能返回多行或零行结果——这是普通 JOIN 做不到的。比如调用表值函数(TVF)时参数来自左表字段:

SELECT o.OrderID, d.ProductID FROM Orders o CROSS APPLY dbo.GetOrderDetails(o.OrderID) d;

如果把 GetOrderDetails 换成普通表,sql Server 通常会自动将 CROSS APPLY 优化为等价 INNER JOIN,性能无差异;但一旦右侧是 TVF、子查询含外部引用,或需 TOP/ORDER BY 限定每行对应结果(如取每个用户的最新一条日志),CROSS APPLY 就不可替代。

OUTER APPLYLEFT JOIN 的行为差异在哪

表面看 OUTER APPLYLEFT JOIN,但关键区别在右侧表达式的执行时机和空值处理逻辑:

  • 右侧是 TVF 或含相关子查询时,LEFT JOIN 要求该对象能被提前“物化”(即不依赖左表当前行),否则报错;OUTER APPLY 允许逐行调用,未匹配时右侧列全为 NULL
  • 右侧若含 TOP 1 ORDER BY(如取每个订单最贵的项),LEFT JOIN 无法直接表达这种“每行独立 Top”语义,而 OUTER APPLY 天然支持
  • 性能上,二者执行计划常相似,但若右侧逻辑复杂(如嵌套循环中反复执行 TVF),OUTER APPLY 可能比试图强行改写成 LEFT JOIN + ROW_NUMBER() 更直观且易维护

CROSS APPLYOUTER APPLY 的性能陷阱

真正拖慢查询的往往不是关键字本身,而是右侧表达式的执行方式:

  • 右侧 TVF 若是非内联(multi-statement)函数,每次调用都会产生额外开销,且 SQL Server 难以准确估算行数,可能导致错误的连接算法选择
  • 若左侧大表(百万行)+ 右侧每次调用都扫描一个大表,实际执行可能是嵌套循环 × N 次全表扫描,比先 JOIN 再过滤慢几个数量级
  • OUTER APPLY 在右侧无匹配时仍要保留左表行,若后续还有其他 WHERE 条件作用于右侧列(如 WHERE d.Amount > 100),SQL Server 可能无法下推谓词,导致先生成大量 NULL 行再过滤

建议用 SET STATISTICS xml ON 查看执行计划中右侧操作的实际执行次数和预估/实际行数是否严重偏离。

如何判断该用 APPLY 还是改写为传统 JOIN

优先选 APPLY 的场景很明确:

  • 右侧必须引用左侧列(尤其 TVF、CTE、子查询含 o.ID 等)
  • 需要“对左表每一行,独立执行一次右侧逻辑”(如分页取 Top N、动态计算列、jsON 解析)
  • 右侧返回结果集结构不固定(如 OPENjson 解析不同格式)

反之,如果右侧是静态表或视图,且关联条件简单,硬套 APPLY 不仅没优势,还可能干扰优化器。另外注意:SQL Server 2016+ 对内联 TVF 的优化已大幅增强,但旧版本中非内联 TVF + APPLY 仍是典型性能雷区。

text=ZqhQzanResources