我有一个与Oracle查询相关的性能问题,其中成本会根据我如何设置time_interval值而发生显著变化。
当我硬编码time_interval值时(例如,WHERE o.request_time > SYSDATE - 15/1440),执行计划显示它使用了索引,并且成本很低。
然而,当我使用来自联接表的动态值时(例如,WHERE o.request_time > SYSDATE - c.time_interval/1440),执行计划不使用索引,成本会显著增加。
下面是我的查询的简化版本:
SELECT ...
FROM table_1 o
,table_2 c
WHERE o.request_time > SYSDATE - c.time_interval/1440;
在表1中,请求时间被索引。
我期望执行计划一致地使用索引,而不管time_interval是静态的还是从联接表中动态获取的。我的主要目标是理解为什么动态time_interval会阻止优化器使用索引,并优化查询以确保持续的低成本。
这是实际查询:
SELECT c.state
,c.critical_msg_quotes
,COUNT(1) AS critical_msg_count
FROM operations o
,configuration c
WHERE o.state = c.state
AND o.request_time > sysdate - c.time_interval / 1440
GROUP BY c.state
,c.critical_msg_quotes
2条答案
按热度按时间cwtwac6a1#
当您使用像
SYSDATE - 15/1440
这样的文字时,Oracle可以查询列high_value
、low_value
、num_distinct
和统计数据收集(DBA_TAB_COL_STATISTICS
)收集的直方图,以计算您的行中可能有多少百分比的日期在这个狭窄的范围内。使用这些信息,它可以确定您需要相对于整个表的非常少量的行,这使得使用索引成为一个更有吸引力的选择。但是,当您使用来自其他地方的值时,Oracle无法预测该值将是什么,因为它取决于两个表之间的连接结果。在这种情况下,优化器对基数做了更宽松的假设,并且它的计算经常会有很大的偏差(数量级上)。如果它相信你的 predicate 将匹配表的重要部分,它将绕过索引并进行全表扫描,如果它的假设是正确的,这将是最好的方法。当您需要的不仅仅是表的一个非常小的切片时,完全扫描比使用索引更好。甲骨文只是试图根据它所知道的有限的东西来选择最好的。
你知道一些它不知道的事:该连接的结果将给予
time_interval
值,该值将驱动非常小的时间窗口。要解决这个问题,您应该在(state,request_time)
上按该顺序建立一个两列索引,并向查询添加一个提示:最后,请忽略
COST
,它在性能调优中没有任何作用。如果Oracle犯了一个错误,根据定义,它对COST
的计算是不正确的,所以这个值应该没有任何意义。Oracle将 * 始终 * 选择具有最低COST
值的计划。但这是一个很难得到正确的数字。mu0hgdu02#
request_time
上的索引不适合您的查询,您需要state,request_time
上的索引如果你有一个最新版本的Oracle(>= 12),你应该看到一个 * 自适应计划 *,它可以在哈希连接和对操作表上的索引范围扫描的嵌套循环的完全访问之间切换-参见下面的示例
如果你的查询仍然有性能问题,下一个选择是对操作表进行 * 范围分区 *(如果是这样,问一个新的问题)。