自适应联接需同时满足三条件:兼容级别≥150、批处理模式(列存储索引或tf 11256)、执行计划中实际出现该算子;它依赖运行时反馈,非hint可控,且对apply类失真场景敏感。

自适应联接不是开关,得先满足三个硬条件
直接在存储过程中加 OPTION (USE HINT('ENABLE_QUERY_OPTIMIZER_HOTFIXES')) 没用。自适应联接(Adaptive Join)是执行时动态决策机制,它不靠 hint 触发,而是依赖底层运行环境是否就绪。必须同时满足:
- 数据库兼容性级别设为
150或更高:执行ALTER DATABASE [your_db] SET COMPATIBILITY_LEVEL = 150;,仅改实例默认值无效 - 查询走批处理模式(Batch Mode):要么表上有列存储索引(哪怕只建一个非聚集列存储索引),要么启用 trace flag
11256(仅限企业版/云环境),行模式(Row Mode)下该功能完全不激活 - 执行计划中实际出现
Adaptive Join算子:不是所有 JOIN 都能触发,典型场景是CROSS APPLY或OUTER APPLY后接估算严重失真的内侧(比如 MSTVF 返回 50 万行但预估仅 100 行)
存储过程里怎么让 APPLY 查询“够格”触发自适应联接
自适应联接对子查询类逻辑最敏感,但普通 WHERE id IN (SELECT ...) 不会触发。你得把子查询显式转成 APPLY 形式,并控制内侧数据量特征:
- 把多语句表值函数(MSTVF)用于 APPLY:例如
SELECT * FROM Orders o OUTER APPLY dbo.GetLastOrderDetails(o.OrderID) AS d,且函数体内不能含 DML、临时表或 DDL - 确保外层驱动表(如
Orders)有合理筛选条件,避免全表扫描——否则即使触发自适应,切换阈值也难生效 - 内侧估算与实际偏差需 > 2 倍,且总行数 > 10000:可通过
DBCC SHOW_STATISTICS查看直方图,或用QUERY_STORE对比实际 vs 预估行数 - 执行后务必清空计划缓存:
DBCC FREEPROCCACHE;,否则旧计划继续复用,根本看不到自适应行为
为什么加了 OPTION(RECOMPILE) 反而可能禁用自适应
OPTION (RECOMPILE) 强制每次重编译,听起来很“智能”,但它会绕过查询存储的运行时反馈路径,导致交错执行(Interleaved Execution)失效——而交错执行正是 MSTVF 场景下提供真实行数给自适应联接的关键前置步骤。
更隐蔽的问题是:若存储过程中混用 sp_executesql 动态拼接 + RECOMPILE,SQL Server 可能退回到行模式执行,直接跳过批处理模式下的自适应能力。建议优先用参数化静态 SQL,仅在必要时加 OPTION (USE HINT('DISABLE_BATCH_MODE_ADAPTIVE_JOINS')) 来做对照测试。
查不到 Adaptive Join 算子?先看执行计划底部有没有 Batch Mode 标记
打开实际执行计划(不是估计计划),右键任意算子 → “属性”,滚动到底部找 Actual Execution Mode。如果显示 Row,那无论兼容级别多高、函数多规范,Adaptive Join 都不会出现。
常见漏点:
- 没建任何列存储索引:哪怕只是
CREATE NONCLUSTERED COLUMNSTORE INDEX IX_cs ON dbo.Orders(OrderID)也能激活批处理模式 - WHERE 条件用了非 SARGable 表达式(如
WHERE YEAR(OrderDate) = 2024),导致优化器放弃列存储扫描路径 - 连接字段类型不一致(如
intvsbigint),隐式转换阻断批处理模式下推
自适应联接不是银弹,它只在特定失真场景下“中途换路”。真正稳的优化,还是得靠索引覆盖、统计信息更新和 MSTVF 内部逻辑精简——算子能切换,不代表你该依赖它来救烂设计。











