我有一个Oracle查询,如下所示需要10分钟或更长时间才能运行:
select
r.range_text as duration_range,
nvl(count(c.call_duration),0) as calls,
nvl(SUM(call_duration),0) as total_duration
from
call_duration_ranges r
left join
big_table c
on c.call_duration BETWEEN r.range_lbound AND r.range_ubound
and c.aaep_src = 'MAIN_SOURCE'
and c.calltimestamp_local >= to_date('01-02-2014 00:00:00' ,'dd-MM-yyyy HH24:mi:ss')
AND c.calltimestamp_local <= to_date('28-02-2014 23:59:59','dd-MM-yyyy HH24:mi:ss')
and c.destinationnumber LIKE substr( 'abc:1301@company.com:5060;user=phone',1,8) || '%'
group by
r.range_text
order by
r.range_text
如果我将查询的日期部分更改为:
(c.calltimestamp_local+0) >= to_date('01-02-2014 00:00:00' ,'dd-MM-yyyy HH24:mi:ss')
(AND c.calltimestamp_local+0) <= to_date('28-02-2014 23:59:59','dd-MM-yyyy HH24:mi:ss')
它在2秒内运行。我基于另一篇文章做了这个,以避免使用日期索引。虽然看起来很直观 - 索引使事情变得如此之慢。
执行解释计划,新查询和更新查询之间似乎相同。唯一的区别是MERGE JOIN操作在旧查询中是16,269字节,在新查询中是1,218字节。实际上,旧查询中的基数也更高。而我实际上并没有看到&#34; INDEX&#34;对解释计划中的旧查询或新查询的操作,仅针对destinationnumber字段上的索引。
那么为什么索引如此放慢了查询的速度呢?我能对指数做些什么 - 不要以为使用&#34; + 0&#34;是最好的解决方案......
查询两天的数据,禁止使用destinationnumber索引:
0 SELECT STATEMENT ALL_ROWS 329382 1218 14
1 SORT GROUP BY 329382 1218 14
2 MERGE JOIN OUTER 329381 1218 14
3 SORT JOIN 4 308 14
4 TABLE ACCESS FULL CALL_DURATION_RANGES ANALYZED 3 308 14
5 FILTER
6 SORT JOIN 329377 65 1
7 TABLE ACCESS BY GLOBAL INDEX ROWID BIG_TABLE ANALYZED 329376 65 1
8 INDEX RANGE SCAN IDX_CDR_CALLTIMESTAMP_LOCAL ANALYZED 1104 342104
使用destinationnumber索引查询2天:
0 SELECT STATEMENT ALL_ROWS 11 1218 14
1 SORT GROUP BY 11 1218 14
2 MERGE JOIN OUTER 10 1218 14
3 SORT JOIN 4 308 14
4 TABLE ACCESS FULL CALL_DURATION_RANGES ANALYZED 3 308 14
5 FILTER
6 SORT JOIN 6 65 1
7 TABLE ACCESS BY GLOBAL INDEX ROWID BIG_TABLE ANALYZED 5 65 1
8 INDEX RANGE SCAN IDX_DESTINATIONNUMBER_PART ANALYZED 4 4
查询一个月,抑制destinationnumber索引 - 完全扫描:
0 SELECT STATEMENT ALL_ROWS 824174 1218 14
1 SORT GROUP BY 824174 1218 14
2 MERGE JOIN OUTER 824173 1218 14
3 SORT JOIN 4 308 14
4 TABLE ACCESS FULL CALL_DURATION_RANGES ANALYZED 3 308 14
5 FILTER
6 SORT JOIN 824169 65 1
7 PARTITION RANGE ALL 824168 65 1
8 TABLE ACCESS FULL BIG_TABLE ANALYZED 824168 65 1
答案 0 :(得分:2)
虽然看起来很直观 - 索引减慢了很多东西。
只有在您不了解索引的工作方式时才会出现反直觉。
索引适用于检索单个行。它们不适合检索大量记录。您还没有提供任何指标,但您的查询似乎可能涉及大量行。在这种情况下,全表扫描或其他基于set =的操作将更有效。
调整日期范围查询很棘手,因为无论我们的统计数据是多么最新,数据库都很难知道两个边界之间有多少记录。 (当日期界限可能变化时调整甚至更棘手 - 一天与一个月或一年不同。)因此,我们经常需要通过使用我们对数据的了解来帮助优化器。
不要使用&#34; + 0&#34;是最好的解决方案......
为什么不呢?人们一直在使用这种技术来避免在几十年内在特定查询中使用索引。
然而,还有更现代的解决方案。未记录的基数提示是一个:
select /*+ cardinality(big_table,10000) */
...应该足以阻止优化器使用索引 - 只要您为查询中的表所有收集了准确的统计信息。
或者,您可以强制优化器使用...
进行全表扫描 select /*+ full(big_table) */
无论如何,你无法对索引做任何改变数据库工作方式的事情。您可以通过分区来加快速度,但我猜您的组织是否已经购买了Partitioning option您已经使用过它。
答案 1 :(得分:2)
这些是使用索引减慢查询速度的原因:
完整的表扫描会更快。如果必须检索大部分行,则会发生这种情况。具体数字取决于各种因素,但如果您检索超过10-20%的行,使用索引的常见情况下的经验法则较慢。
使用其他索引会更好,因为在第一阶段之后剩下的行数会减少。在表上使用某个索引通常意味着不能使用其他索引。
现在决定哪种变体是最好的是优化工作。要执行此任务,他必须猜测(除其他事项外)应用某些过滤子句后剩余多少行。此估计值基于表统计信息,通常很好。它甚至会考虑到偏斜的数据,但如果您的statitiscs过时或者您的数据分布相当罕见,它可能会关闭。例如,如果您在示例中插入了二月的数据之前计算了统计信息,则优化程序可能会错误地断定在应用日期范围过滤器后只剩下很少(如果有)行。
在多个列上使用组合索引也可能是一个取决于您的数据的选项。
关于&#34;偏斜数据问题的另一个注意事项&#34;:如果您在cloumn A上有索引,优化程序会检测到A列中的偏斜数据,但如果您在A列和A列上只有一个组合索引则不会B,因为组合可能使分布更均匀。这是A,B上的索引没有在A冗余上建立索引的少数情况之一。
APC答案显示如果使用正确的统计数据仍然会产生错误的计划,如何使用提示将优化器指向正确的方向。