CROSS APPLY与OUTER APPLY性能差异问询:全匹配场景下为何前者更快
先来看你对比的两段SQL脚本:
UPDATE t SET Amount = applied.MaxAmount FROM @test t CROSS APPLY ( SELECT MAX(t2.Amount) AS MaxAmount FROM @test t2 WHERE t2.Id = t.ForeignId ) AS applied
vs
UPDATE t SET Amount = applied.MaxAmount FROM @test t OUTER APPLY ( SELECT MAX(t2.Amount) AS MaxAmount FROM @test t2 WHERE t2.Id = t.ForeignId ) AS applied
你说得没错——这两段脚本的功能完全一致。因为MAX()作为聚合函数,哪怕子查询里没有匹配到任何行,也会返回一个NULL结果,所以不管用CROSS APPLY还是OUTER APPLY,最终都会更新@test表的每一行,结果没有区别。但大数据集下OUTER APPLY跑得慢很多,核心原因在于两者的执行逻辑和查询优化器的处理方式不同:
过滤与保留逻辑的本质差异
CROSS APPLY的语义更接近INNER JOIN,而因为你用了MAX(),子查询永远会返回至少一行(要么是实际的最大值,要么是NULL),所以查询优化器能直接推断出:主表的每一行都能匹配到子查询的结果。基于这个推断,优化器会生成更紧凑的执行计划,省去了OUTER APPLY必须做的“检查主表行是否有匹配、无匹配则保留原行”的额外步骤。
而OUTER APPLY的语义是强制保留主表所有行,哪怕子查询无结果——哪怕你的场景里子查询不可能无结果,优化器还是会按照通用的OUTER APPLY逻辑来处理,这就带来了不必要的行状态维护和匹配检查,大数据集下这些额外操作的开销会被显著放大。查询优化器的优化优先级
SQL Server对CROSS APPLY的优化支持更成熟,它可以复用很多针对内连接的优化规则,比如高效的嵌套循环连接、精准的索引查找、对统计信息的充分利用。而OUTER APPLY因为要兼容“无匹配行”的场景,优化器会做更多保守的处理,比如选择开销更高的连接方式(比如哈希连接),或者增加额外的执行步骤来确保主表行不丢失,这自然会拖慢速度。行数预估的准确性
对于CROSS APPLY,优化器能准确预估子查询返回行数是1行,从而选择最适合的执行策略;而OUTER APPLY的行数预估会更保守(默认可能考虑子查询返回0行或多行的情况),错误的预估会导致优化器选择低效的执行计划,在大数据量下这种影响尤为明显。
简单来说:虽然功能等效,但CROSS APPLY的语义给了查询优化器更多优化空间,避免了OUTER APPLY的额外逻辑开销,所以在大数据集下性能更优。
内容的提问来源于stack exchange,提问作者Kyle

