SQL中OR条件下为何触发全表扫描而非索引查找?能否无需UNION优化?
跨表OR条件与UNION查询的执行计划差异解析
一、为什么OR条件会触发全表扫描
跨表OR的逻辑限制
你的OR条件分别属于关联的两张表:hdr.SalesOrderID = 43659针对主表,dtl.SalesOrderDetailID = 43659针对子表。SQL优化器的索引查找逻辑是基于单表维度的,它无法同时利用两张表的索引构建高效执行计划——JOIN本身已经在关联两张表,跨表OR相当于要求“要么主表ID匹配,要么子表ID匹配”,这种复合逻辑无法直接映射到现有索引的查找路径,最终优化器只能选择全表扫描两张表后再过滤符合条件的行。成本估算的选择
优化器会对比不同执行计划的成本。当跨表OR条件出现时,优化器可能认为:拆解条件、分别走索引再合并结果的成本,比直接全表扫描更高(尤其是表数据量较小时),因此不会主动将OR查询优化为类似UNION的索引查找逻辑。
二、无需改写为UNION的替代方案
可以通过先筛选目标ID再关联的方式,让优化器分别利用两张表的索引,避免全表扫描:
SELECT DISTINCT hdr.*, dtl.* FROM ( -- 索引查找主表符合条件的订单ID SELECT SalesOrderID FROM [AdventureWorks2022].[Sales].[SalesOrderHeader] WHERE SalesOrderID = 43659 UNION ALL -- 索引查找子表目标行对应的订单ID SELECT dtl.SalesOrderID FROM [AdventureWorks2022].[Sales].[SalesOrderDetail] dtl WHERE dtl.SalesOrderDetailID = 43659 ) AS filtered_ids JOIN [AdventureWorks2022].[Sales].[SalesOrderHeader] hdr ON hdr.SalesOrderID = filtered_ids.SalesOrderID JOIN [AdventureWorks2022].[Sales].[SalesOrderDetail] dtl ON hdr.SalesOrderID = dtl.SalesOrderID
这个写法的核心是先通过两个独立的索引查找得到所有符合条件的SalesOrderID,再用这些ID关联两张表,全程都会利用索引;DISTINCT用于避免重复行,和原UNION的去重效果一致。如果能确认两个条件不会返回重复行,可将DISTINCT换成UNION ALL进一步提升性能。
内容的提问来源于stack exchange,提问作者J. Moore
相关产品推荐
相关产品推荐

