SQL Server 2019 的智能查询处理(IQP)不直接重写子查询,而是通过兼容性级别150启用交错执行、自适应联接等机制,提升MSTVF和APPLY类子查询的实际行数估算与执行决策准确性。

SQL Server 2019 的智能查询处理(IQP)本身**不直接“自动优化子查询”**,它不会重写你的 WHERE ... IN (SELECT ...) 或 EXISTS 语句。所谓“自动优化”,其实是通过底层执行机制的增强,让原本容易出错的子查询执行路径变得更可控、更可预测——前提是数据库兼容性级别设为 150。
为什么 SET COMPATIBILITY_LEVEL = 150 是前提?
不设这个,IQP 功能基本不生效。SQL Server 2019 的 IQP 系列功能(包括自适应联接、内存授予反馈、交错执行)全部绑定在兼容性级别 150 及以上。低于该级别,优化器仍走旧路径,对相关子查询的估算仍是固定规则(比如一律按 100 行预估),极易导致嵌套循环失控或内存分配偏差。
-
ALTER DATABASE [your_db] SET COMPATIBILITY_LEVEL = 150;必须执行,且不能只靠实例级默认值 - 改完后需清空计划缓存:
DBCC FREEPROCCACHE;,否则旧计划可能继续复用 - 某些功能(如交错执行)还要求对象启用
QUERY_STORE,否则无法收集运行时行数反馈
自适应联接如何间接改善子查询性能?
当子查询被重写为 CROSS APPLY 或 OUTER APPLY 时,执行计划里常出现 Nested Loops 联接。在 COMPATIBILITY_LEVEL = 150 下,若内侧(子查询侧)实际返回行数远超预估,SQL Server 可在运行时中途切换为 Hash Join——避免外层每行都触发一次子查询扫描。
- 仅对批处理模式(Batch Mode)生效,所以列存储索引或启用了行存储批处理模式(
trace flag 11256或 Azure SQL)才起作用 - 普通行模式(Row Mode)下,
APPLY依然走 Nested Loops,IQP 不干预 - 不是所有
APPLY都能触发自适应;必须满足:内侧估算行数与实际偏差 > 2 倍,且总行数 > 10000
交错执行(Interleaved Execution)专治 MSTVF + 子查询嵌套
如果你把子逻辑封装进多语句表值函数(MSTVF),再在主查询里调用,SQL Server 2017 之前会按固定 100 行估算——哪怕函数实际返回 50 万行,外层 JOIN 就全乱套。开启 COMPATIBILITY_LEVEL = 150 后,SQL Server 会先执行 MSTVF、拿到真实行数,再编译外层计划。
- 仅适用于 MSTVF,单语句 TVF(STVF)和内联 TVF(ITVF)不受影响
- 函数体内不能含 DML、临时表、DDL,否则无法启用交错执行
- 错误示例:
WHERE id IN (SELECT id FROM dbo.MyMstvf(@param))—— 若MyMstvf返回行数严重失真,交错执行就是救命稻草
真正要动手的地方,还是执行计划里的 Rebinds 和 Spool
IQP 不会帮你把 IN (SELECT ...) 自动转成 JOIN,也不会消除 Compute Scalar + Table Spool。它只是让这些节点的行为更“诚实”:Rebinds 值更准、内存授予更稳、Spool 是否物化由真实数据驱动而非拍脑袋估算。最终是否快,取决于你是否愿意看懂 SET STATISTICS PROFILE ON 输出里的 Rebinds 和 EstimateRebinds——这才是最常被跳过的动作。

















