我有一个像这样的分区表:
create table demo (
ID NUMBER(22) not null,
TS TIMESTAMP not null,
KEY VARCHAR2(5) not null,
...lots more columns...
)
分区位于TS
列(每年一个分区)。
由于我通过时间戳进行了大量搜索,因此我创建了一个组合索引:
create index demo.x1 on demo (ts, key);
查询如下所示:
select *
from demo t
where t.TS = to_timestamp('2009-06-30 07:47:57', 'YYYY-MM-DD HH24:MI:SS')
我还尝试添加and t.KEY = '00101'
,但这没有帮助。
但EXPLAIN PLAN
表示TABLE ACCESS
和FULL
:
# Operation Options Object Mode Cost Bytes Cardinality
0 SELECT STATEMENT ALL_ROWS 583804 287145 2127
1 PARTITION RANGE ALL 583804 287145 2127
2 TABLE ACCESS FULL HEADER ANALYZED 583804 287145 2127
没有提到指数。可能有什么不对?
[编辑]出于某种原因,甲骨文完全错误地计算了该操作的成本。我在该表中有1.12亿行。完全扫描单个分区的成本应该是2000万,而不是600'000。这就是它甚至忽略优化器提示的原因。
[EDIT2]在我的测试中,我跑过这个令人费解的结果。当我运行此select
时:
select tx_ts
from kt.header
where tx_ts = to_timestamp('2009-06-30 07:47:57', 'YYYY-MM-DD HH24:MI:SS')
我得到了这个解释计划:
0 SELECT STATEMENT ALL_ROWS 152 15616 1952
1 PARTITION RANGE ALL 152 15616 1952
2 INDEX FAST FULL SCAN HEADERX2 ANALYZED 152 15616 1952
因此,当我将自己限制为索引列作为select
的结果时,Oracle决定使用该索引。当我想获得所有列时,我必须等待全表扫描。这是怎么回事?
[EDIT2]找到了;请参阅下面的答案。
答案 0 :(得分:5)
好的,我的错误是:该列的类型为DATE
,而不是TIMESTAMP
。自从我使用to_timestamp()
以来,Oracle没有办法使用索引。
答案 1 :(得分:1)
您的统计信息是最新的吗?无效的统计信息可能意味着oracle认为全表扫描比使用索引更快。您是否在查询中使用任何可能告诉oracle进行全面扫描的提示?
您能为我们提供完整的查询并解释计划结果吗?
编辑:Aaron,您可以使用“dbms_stats.gather_schema_stats”或“dbms_stats.gather_table_stats”命令更新统计数据。有关命令的更多信息,请参阅here。这将更新指定的架构或表的所有相关统计信息。 Oracle的基于成本的优化器将使用统计信息来确定要选择的执行计划。它从不使用实际的表大小。当桌子大小发生显着变化(+/- 10%左右)时,您需要重新更新统计数据
另一件事。当您使用复合索引时,您需要在查询中指定索引中使用的所有列,以便优化器考虑索引(我认为您需要以相同的顺序指定它们,尽管我可能错了那个,我看了这个东西已经有一段时间了)
答案 2 :(得分:1)
您发布的“CREATE INDEX ...”语句的转录中可能只有拼写错误,但您确定您确实创建了索引吗?
为了给我们一些关于统计数据的第一步概念,请使用以下查询:
select table_name, num_rows
from user_tables
where table_name = 'DEMO';
select table_name, num_rows
from user_tab_partitions
where table_name = 'DEMO';
select index_name, num_rows from user_indexes
where table_name in
(select table_name
from user_tables where table_name = 'DEMO');
另外,您究竟如何生成EXPLAIN PLAN?如果启用跟踪,是否可以访问数据库主机以检索跟踪文件?
[编辑] 正如我评论的那样,查看实际执行查询的跟踪会很好。由于您已表明可以访问db主机文件系统,因此请运行(在同一会话中)发出以下内容的SQL脚本:
alter session set sql_trace=true;
select /* THIS IS THE TRACE */
*
from demo t
where t.TS = to_timestamp('2009-06-30 07:47:57', 'YYYY-MM-DD HH24:MI:SS');
exit
脚本退出后,找出答案 跟踪文件目录所在的位置 这个查询:
从v $ parameter中选择值,其中name ='user_dump_dest';
使用任何搜索工具 可以找到该文件 其中包括字符串“这就是了 TRACE“
处理/配置跟踪文件 发出OS命令 tkprof traceFileName.trc tkprof.out
检查此文件 - 您将看到一些开销信息,但会有一个部分详细说明查询的实际执行计划和统计信息。如果您在此信息中看到相同的结果,则下一步是添加另一个语句(在第一个“更改会话”之后),该语句将转储有关Oracle CBO忽略索引的原因的信息:
ALTER SESSION SET EVENTS='10053 trace name context forever, level 1';
答案 3 :(得分:1)
我不是分区专家,但我认为这里发生的事情是你创建了一个全局索引 - 一个覆盖所有分区中行的索引。因此,优化器必须在两个互斥的访问路径之间进行选择:(A)索引范围扫描,或(B)分区修剪。我相信PARTITION RANGE操作表明它选择了B.
正如其他人所建议的那样,更新统计信息可能会改变行为。删除并重新创建索引时,将丢弃该索引存在的所有统计信息。
将索引创建为UNIQUE,如果时间戳和键唯一标识一行,那将是一个好主意,也可能会改变行为。
但是,我认为真正的“修复”是您应该创建本地索引 - 每个分区上的单独索引。这应该使优化器能够进行分区修剪,然后进行索引查找。老实说,我不确定这样做的确切语法是什么。也许您只需使用各个分区名称明确地在每个分区上创建索引。
答案 4 :(得分:1)
如果其他一切都失败了,您可以尝试优化器提示:
select /*+ index(demo.demo demo.x1) */ *
from demo t
where t.TS = to_timestamp('2009-06-30 07:47:57', 'YYYY-MM-DD HH24:MI:SS')