SQL Server中INNER JOIN与WHERE IN性能差异巨大的原因探究
这事儿核心原因是SQL Server查询优化器对两种写法的执行策略判断完全不同,尤其是有没有用上你给视图创建的聚集索引IX_AlerteVehicule,咱们拆开说:
1. WHERE IN 写法完美贴合视图的索引设计
你的视图AlertesVehicules是带聚集索引的索引视图,索引的最左前缀列是EntiteGestion——这刚好是你要过滤的第一个条件。当你用a.EntiteGestion IN (SELECT value FROM STRING_SPLIT(...))时,优化器一眼就认出可以直接用这个索引的最左列做精准过滤:
- 先通过聚集索引查找,快速定位到所有符合指定
EntiteGestion的行,结果集一下子就缩小了 - 接着再用
CodeAlerte IN ('P08','P09')等条件进一步过滤,后续关联tb_AFFECTATION_SECTION的操作也是基于小结果集,自然速度快(10ms级别)
2. INNER JOIN 写法触发了视图的“展开”,没用到索引视图的优势
当你把STRING_SPLIT的结果用INNER JOIN和视图关联时,优化器没有把这个JOIN条件和视图的过滤逻辑合并,反而选择先展开视图的底层查询:也就是先去扫dbo.Alerte表(输出369000行),再去和拆分后的EntiteGestion列表做关联过滤。这相当于先把大结果集拉出来再筛选,完全浪费了视图上预聚合和聚集索引的优化,所以耗时直接涨到100ms。
另外,这种JOIN写法可能让优化器放弃使用索引视图的预计算结果,转而重新计算视图的底层关联,进一步放大了性能差距。
3. LEFT JOIN的条件也间接影响了执行顺序
两个查询里的LEFT JOIN tb_AFFECTATION_SECTION加上@currentDate <= ISNULL(ase.DateFin, @currentDate)的条件,虽然逻辑上不改变结果,但优化器在处理两种写法时的执行顺序不同:
- WHERE IN写法:先通过索引视图拿到小结果集,再去关联
tb_AFFECTATION_SECTION,关联的数据量小 - JOIN写法:先拉取视图的全量(或大数量)结果,再去关联,关联的数据量直接大了一个数量级
总结建议
优先保留WHERE IN子查询的写法,它更符合SQL Server优化器的预期,能最大化利用你创建的索引视图。如果一定要用JOIN写法,可以尝试在查询末尾加上OPTION(RECOMPILE),让优化器重新评估执行计划,看看能不能触发索引视图的使用,但通常WHERE IN的写法稳定性更好。
内容的提问来源于stack exchange,提问作者Théophane

