MSSQL优化之 or 跟 union all 案例
两个MSSQL版本:
1.Microsoft SQL Server 2008 R2 (SP3-GDR) (KB4057113) - 10.50.6560.0 (X64) Dec 28 2017 15:03:48 Copyright (c) Microsoft Corporation Web Edition (64-bit) on Windows NT 6.3 <X64> (Build 9600: ) (Hypervisor)
2.Microsoft SQL Server 2008 R2 (RTM) - 10.50.1600.1 (Intel X86) Apr 2 2010 15:53:02 Copyright (c) Microsoft Corporation Enterprise Edition on Windows NT 6.2 <X64> (Build 9200: ) (WOW64)
SQL伪代码:
select * from tabla where (@flag=0 and col=xxx) or (@flag=1 and EXISTS(select top 1 1 from table b where a.col2=b.col))
在SP3-GDR系统上用or的写法要1-3秒在RTM1秒内就出来了。
一般这种情况优化很简单,就是直接把不同的条件拆成union all 独立出来。
还有一个在不改变写法的前提下直接在后面加上 OPTION (RECOMPILE) 就可以解决
select * from tabla where (@flag=0 and col=xxx) or (@flag=1 and EXISTS(select top 1 1 from table b where a.col2=b.col)) OPTION (RECOMPILE)
根据AI的分析:
当不使用 RECOMPILE 时,SQL Server 生成的是一个通用的执行计划,它必须同时应对 @flag=0 和 @flag=1 两种情况。于是优化器只能用 Left Semi Join + Concatenation(串联两个分支)这种安全但笨重的方式,因为你看到的嵌套循环就是这种通用计划的产物。
而加上 RECOMPILE 后,每一次执行都会重新编译,优化器可以看到变量的实际值。当 @flag=0 时,编译期就会进行“常量折叠”,将整个 (@flag=1 AND EXISTS(...)) 分支直接移除,剩下就是对 sflr 直接做日期范围查询,计划变得非常简洁高效(就像你 UNION ALL 写法中的第一个分支一样)。这等于每次编译出一个“定制版”的计划,自然快。
根据情况来优化,最好还是改成union all
2. OPTION (RECOMPILE) 的弊端
它不是免费的,主要代价如下:
① CPU 与时间开销 —— 每次都硬编译
-
执行计划不会被缓存,每次执行都要经历完整的编译过程(解析、代数化、优化)。
-
如果你的查询是高频执行(例如每秒钟几百上千次),累计的编译 CPU 消耗会明显推高服务器负载。
② 计划缓存完全失效 —— 无法重用
-
正常的参数化查询可以缓存在计划缓存中供后续执行重用,
RECOMPILE会跳过缓存,导致计划缓存碎片多,也影响sys.dm_exec_query_stats之类的 DMV 对该查询的统计跟踪(记录不到执行历史)。
③ 无法利用“参数嵌入”以外的优化
-
虽然对当前这个
@flag的分支问题有效,但如果查询中有其他复杂谓词或需要动态筛选的场景,它会强制每次生成新计划,可能错过一些更轻量的方案(比如OPTIMIZE FOR提示)。
④ 在极高并发下可能引发编译阻塞
-
同时进来大量相同的查询请求,如果都带着
RECOMPILE,会瞬间产生大量并发编译,造成明显的 CPU 尖峰和内存分配压力(虽然 SQL Server 对编译有一定并发控制,但依然有风险)。
⑤ 无法用于某些只读或受限环境
-
这不是问题,但需要知道的是,
OPTION (RECOMPILE)是 T-SQL 级别的提示,如果查询是构建在 ORM 或代码中拼装的,要确保提示能被正确嵌入
如何权衡?两个终极方案
根据你的查询特性(参数通常固定为 @flag=0 或 @flag=1),下面的代价会更小,也更根本:
| 方案 | 优势 | 适用场景 |
|---|---|---|
改写为 UNION ALL |
无额外编译开销,计划可重用,分治后每个分支都可以用最优索引 | 推荐作为最终生产写法 |
OPTION (RECOMPILE) |
改动最小,快速修复 | 适合低频调用的即席查询、调试期,或无法修改 SQL 结构的场景 |
如果你可以在应用代码或存储过程中修改 SQL,强烈建议用 UNION ALL 替代 OR,这会让优化器从一开始就为两个分支分别生成独立高效的子计划,而不会产生任何额外编译开销。
如果实在无法修改语句结构,那么请评估执行频率:
-
每分钟调用几次 →
RECOMPILE完全可接受; -
每秒钟上百次 → 考虑改写为
IF @flag=0 ... ELSE ...两条独立语句,或使用动态 SQL 拼接,避免编译开销。

浙公网安备 33010602011771号