我正在尝试优化以下查询:
SELECT tickstime AS time,
quantity1 AS turnover
FROM cockpit_test.ticks
WHERE date_id BETWEEN 20111104 AND 20111109
AND mdc_id IN (297613)
ORDER BY time;
这很简单,但运行大约需要60-90秒。 cockpit_test.TICKS 表包含超过100M的行。它还有 MDC_ID 和 DATE_ID 列的索引。
EXPLAIN PLAN提供以下输出
"-------------------------------------------------------------------------------------------------------"
"| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |"
"-------------------------------------------------------------------------------------------------------"
"| 0 | SELECT STATEMENT | | 26905 | 604K| | 11783 (1)| 00:02:22 |"
"| 1 | SORT ORDER BY | | 26905 | 604K| 968K| 11783 (1)| 00:02:22 |"
"| 2 | TABLE ACCESS BY INDEX ROWID| TICKS | 26905 | 604K| | 11596 (1)| 00:02:20 |"
"|* 3 | INDEX RANGE SCAN | TICKS_MDC_DATE | 26905 | | | 89 (0)| 00:00:02 |"
"-------------------------------------------------------------------------------------------------------"
" "
"Predicate Information (identified by operation id):"
"---------------------------------------------------"
" "
" 3 - access(""MDC_ID""=297613 AND ""DATE_ID"">=20111104 AND ""DATE_ID""<=20111109)"
所以我并不完全确定这意味着什么,但似乎索引正在受到攻击,并且通过索引rowid访问行消耗了大部分时间。
有没有办法让这个查询运行得更快?
UPD
以下是表格定义:
Name Null? Type
----------------------------------------- -------- ----------------------------
DATE_ID NOT NULL NUMBER(38)
MDC_ID NOT NULL NUMBER(38)
TICKSTIME NOT NULL DATE
STATE NOT NULL NUMBER(38)
VALUE1 NOT NULL FLOAT(126)
VALUE2 FLOAT(126)
VOLUME1 FLOAT(126)
VOLUME2 FLOAT(126)
QUANTITY1 NUMBER(38)
QUANTITY2 NUMBER(38)
桌子上有3个索引:
答案 0 :(得分:1)
通常,Oracle不能组合两个单独的索引(除非它们是位图索引而不是“普通”btree索引)。
mdc_id
列是什么?如果有许多不同的值,您可以在mdc_id, date_id
上创建复合索引。
理论上,Oracle可以使用索引来返回已排序的数据。在这种情况下,您的索引应该在mdc_id, date_id, time
。
为什么不在日期列中使用日期数据类型?对于这个特定的查询,它可能没有多大区别,但一般来说,如果使用正确的数据类型,Oracle将能够更好地确定数据的分布。
答案 1 :(得分:1)
我会检查这个解释计划是否有准确的基数估计。当提供多个谓词时,基数很难估计,并且对于如此小的查询和估计的排序大小,执行时间似乎非常高(除非你的存储基础设施严重不足,这也是非常典型的)。
考虑到查询的持续时间,我通过调用动态采样来确保估计是准确的......
SELECT
/*+ dynamic_sampling(4) */
tickstime AS time,
quantity1 AS turnover
FROM
cockpit_test.ticks
WHERE
date_id BETWEEN 20111104 AND 20111109 and
mdc_id IN (297613)
ORDER BY
tickstime;
如果结果是估计的临时空间小于realtiy(并且您可以通过查询V $ SQL_WORKAREA_ACTIVE来检查),那么您可能必须调整会话的内存设置以切换到自动内存管理并增加排序面积大小。