为什么Oracle执行计划仅基于where子句值进行更改?我不是在讨论绑定变量,而是在SQL中使用就地值。
例如,在SQL Developer中运行有关这些查询的说明计划:
select col1, col2 from table1 where col1 >= '4520/85/36 99.99.99'; -- uses index on col1
select col1, col2 from table1 where col1 >= '4520/85/36 00.00.00'; -- full table scan
什么可以导致Oracle选择这样的不同执行计划?仅供参考,表中不存在上述值,但它们代表了一系列数值相关的值。
通过简单地删除另一列,另一个观察结果再次改变了执行计划:
select col1, col2 from table1 where col1 >= '4520/85/36 00.00.00'; -- full table scan
select col1 from table1 where col1 >= '4520/85/36 00.00.00'; -- uses index on col1
如果问题不明确,请告诉我更多有用的信息。
编辑:我重建了表索引并收集了统计信息,我甚至已经删除并重新创建了索引。此表中的行数约为1200万。
答案 0 :(得分:3)
当未使用绑定变量时,where
子句中具有不同数据的类似查询将被视为不同的查询。在这种情况下,将为每个查询独立计算执行计划。
到达执行计划是多个输入的结果,其中一个是Oracle对表中数据分布的理解(直方图,基于收集的最后统计数据)。如果数据分布存在任何偏差(特别是对于索引列),oracle可以根据full table scan
子句中的值选择index scan
或where
。其他因素包括场上索引的存在,桌子的分区等。
此外,删除select子句的列对实际执行计划没有影响,但是Oracle认为它是一个不同的查询并且独立地到达计划。
如果您需要一致的执行计划,那么使用绑定变量是正确的解决方案。此外,您可以在每次触发查询时避免解析查询,计划计算等(因为查询的sql_id保持不变,并且其缓存,计划将不会重新计算)。
另外,要知道,有一个名为绑定峰值的功能,即使使用绑定变量,也会根据绑定变量中的数据影响查询执行计划。
关于这个主题的thread非常有趣的讨论。
答案 1 :(得分:0)
Oracle非常聪明;)它基本上可以计算出获取数据的最佳方式,如果它计算出全表扫描更好,那么它就会使用它。解释计划中的成本是否存在差异?
如果您想为Oracle做出决定,可以使用提示。
答案 2 :(得分:0)
在第一个示例中,第一个where
子句可能仅满足基表中所有行的3%。当您将RHS更改为其他值时,可能有60%的行将满足条件。在第一种情况下,优化器将选择使用索引;在第二种情况下,它可以选择全表扫描(因为使用索引有自己的开销)。此外,真正重要的是优化程序认为将检索多少行;这就是为什么统计数据必须是最新的,以及为什么表中非常均匀分布的数据可能会导致优化器做出奇怪的选择,即使统计数据是最新的。
在第二个示例中,由于您只需要col1
值,并且索引中已存在这些值,因此优化器将仅使用索引,无论返回的行数是多少(因为,甚至当条件满足时,引擎不需要触摸基表;只需col1
的所需值,已从索引中获得)。但是,如果您需要同时检索col1
和col2
,则通过索引查找“匹配”是不够的 - 当找到匹配时,引擎仍需要从基表中检索行阅读col2
值。因此,如果它希望返回相对大部分的行,那么它只会执行全表扫描。
如果您在col1, col2
上有一个复合索引(前导col1
),您可能会看到优化器选择使用该索引,而不管它预期有多少行都不进行全表扫描检索。