cross apply 不可被 inner join 替代,因其右表达式可每行动态计算并引用左表字段,支持 top/order by 等依赖左表值的操作;而 inner join 要求右表为静态集合。

为什么不能直接用 INNER JOIN 替代 CROSS APPLY?
因为 INNER JOIN 要求右表是静态集合,而 CROSS APPLY 允许右表表达式「每行动态计算」——关键在于它能引用左表当前行的列值作为参数。比如你想为每个订单查出「该订单下最贵的3个商品」,INNER JOIN 写不出这种带 TOP 3 + ORDER BY + 左表字段依赖的子查询;但 CROSS APPLY 可以:
SELECT oh.SalesOrderID, oh.OrderDate, top3.ProductID, top3.UnitPrice FROM SalesLT.SalesOrderHeader AS oh CROSS APPLY ( SELECT TOP 3 ProductID, UnitPrice FROM SalesLT.SalesOrderDetail AS od WHERE od.SalesOrderID = oh.SalesOrderID -- 这里引用了 oh 的字段 ORDER BY od.UnitPrice DESC ) AS top3;漏掉
WHERE od.SalesOrderID = oh.SalesOrderID 就会变成全表扫描,性能断崖下跌。表值函数必须带参数才能体现 CROSS APPLY 价值
如果表值函数不接收左表字段作为参数,那它和普通表没区别,CROSS APPLY 就退化成无意义的笛卡尔积或等价于 INNER JOIN。真正发挥优势的场景是:函数逻辑依赖左表某列值。例如:
- 拆分逗号分隔字符串:
CROSS APPLY dbo.SplitString(t.tags, ','),其中t.tags来自左表 - 价格区间推荐:
CROSS APPLY GetRelatedProducts(t.Price),函数内部用@Price * 0.8做范围计算 - 进制转换:
CROSS APPLY dbo.F_TConversion(t.num),把整数转二进制/十六进制多行结果
CROSS APPLY dbo.MyFunc() 等价于 CROSS JOIN,毫无动态性可言。CROSS APPLY 和 OUTER APPLY 的行为差异容易被误判
很多人以为只是「有无 NULL」的区别,其实核心在「是否保留左表无匹配的行」:
-
CROSS APPLY:右表达式返回空结果集 → 整行丢弃(类似INNER JOIN) -
OUTER APPLY:右表达式返回空结果集 → 左表字段照常输出,右表字段全为NULL(类似LEFT JOIN)
- 用
CROSS APPLY查「每个学生最近2次成绩」,但有学生没考试 → 该学生整条记录消失,不是你想要的「补 NULL」效果 - 误把
OUTER APPLY当成「一定比 CROSS APPLY 慢」——实际执行计划中,SQL Server 可能对两者生成相同计划,差别只在最终行过滤逻辑
WHERE 1=0,看结果行数是否等于左表行数。性能陷阱:右表达式没有索引或未限制结果集
CROSS APPLY 是逐行执行的,右表达式若未优化,会引发 N×M 级扫描。常见问题:
- 右子查询没用左表字段做索引查找,却写了
WHERE ... LIKE '%xxx%'导致全表扫描 - 忘了加
TOP或WHERE限制,比如CROSS APPLY (SELECT * FROM BigTable)—— 每行都扫一遍大表 - 表值函数内部没走索引,或用了
SELECT *返回大量无用列
- 右表达式中的
WHERE条件必须包含左表关联字段,且该字段在右表上有索引 - 优先用内联表值函数(
ITVF),避免多语句表值函数(MSTVF),后者无法展开、常导致性能骤降 - 用
SET STATISTICS XML ON看执行计划,确认右表达式是否走了 Seek 而非 Scan











