OR条件导致索引失效表现为执行计划出现Index Scan或Table Scan而非Index Seek,尤其在JOIN后的WHERE中;CASE WHEN可替代但受限于SARGable、NULL处理及可读性;更可靠方案是动态SQL或UNION ALL分路径处理。

OR条件导致索引失效的典型现象
执行计划里出现 Index Scan 或 Table Scan,而不是预期的 Index Seek,尤其当 OR 出现在 JOIN 后的 WHERE 子句中时——比如 WHERE a.status = @status OR @status IS NULL。SQL Server 优化器无法为这种“通配式”条件生成高效查找路径,哪怕字段上有索引,也会退化为扫描。
CASE WHEN 替代 OR 的写法与限制
用 CASE WHEN 把逻辑判断提前到等值比较一侧,强制优化器走索引查找:
WHERE CASE WHEN @JobNo IS NULL THEN a.JobNo ELSE @JobNo END = a.JobNo AND CASE WHEN @GkNo IS NULL THEN a.GkNo ELSE @GkNo END = a.GkNo
这个技巧有效的前提是:每个 CASE 表达式的结果列必须是可 SARGable 的(即能被索引直接匹配)。但要注意:
- 不能用于涉及函数或计算的字段,比如
CASE WHEN @x IS NULL THEN YEAR(create_time) ELSE YEAR(@x) END = YEAR(create_time)会彻底失效 - 如果参数为
NULL且对应字段允许NULL,CASE返回NULL会导致整行被跳过(NULL = anything永远为UNKNOWN),需额外补IS NULL判断 - 多个
OR条件嵌套时,CASE嵌套变深,可读性和维护性快速下降
更可靠的做法:动态拼接 SQL 或 UNION ALL
对高并发、高稳定要求的场景,硬编码 OR 不如分路径处理:
- 在应用层判断参数是否为空,只拼入实际生效的 WHERE 条件,避免让 SQL Server 被迫编译一个“兜底”执行计划
- 若必须单条语句,可用
UNION ALL拆成两支:一支查有值参数,一支查全量(加WHERE @param IS NULL控制分支);注意两支 SELECT 列数、类型、顺序必须严格一致 - JOIN 本身别掺和 OR:把过滤逻辑尽量收在驱动表(小表)上,再用它去驱动大表,避免
LEFT JOIN ... ON ... WHERE (a.x = @x OR @x IS NULL)这类写法——它会让右表无法提前剪枝
JOIN 顺序与 OR 共存时的隐蔽陷阱
当 OR 条件混在多表 JOIN 中,比如 LEFT JOIN b ON a.id = b.aid AND (b.type = @type OR @type IS NULL),问题更严重:
- 这个
OR在ON子句里,会导致 SQL Server 无法下推过滤,b 表可能全量加载后再关联,内存和 I/O 压力陡增 - 即使 b 表有
type索引,也大概率不会被用于查找,因为优化器认为该条件“不笃定” - 真正安全的做法是:把
@type过滤提前到子查询或 CTE 中,先筛出满足条件的 b 表子集,再 JOIN
复杂 JOIN + OR 的组合,本质上是在挑战优化器的路径预判能力。最省心的解法不是绕开 OR,而是让 OR 不出现在关键路径上——参数为空时,就让它不参与 JOIN 或 WHERE。










