2009-10-15 73 views
2

我有一个分区表,如下所示:甲骨文拒绝使用索引

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 PLANTABLE ACCESSFULL

# 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 

指数没有提到。什么可能是错的?

[编辑]出于某种原因,甲骨文完全错误的操作成本。那张桌子里有112万行。全面扫描单个分区的成本应该是2000万,而不是600'000。这就是为什么它甚至忽略优化器提示。

[编辑2]在我的测试中,我跑过了这个令人费解的结果。当我运行这个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,甲骨文决定使用索引。当我想要获得所有列时,我必须等待全表扫描。这里发生了什么?

[EDIT2]找到它;请参阅下面的答案。

+1

有一个你正在运行EXPLAIN PLAN的查询的例子吗? – 2009-10-15 16:04:57

+0

表中有多少数据?另外,它是真的在做一个全表扫描,或者只是扫描一个特定的分区? – 2009-10-15 16:10:30

+0

在这里,你去。当我读它时,它会扫描一切(全部1.1亿行)。 – 2009-10-16 09:26:52

回答

4

好吧,这是我犯的一个错误:列有型DATE,不TIMESTAMP。由于我使用了to_timestamp(),Oracle认为无法使用索引。

+0

非常感谢。同样的问题在这里。最好的问候。 – 2017-10-04 19:05:08

1

你的数据是否是最新的?无效的统计信息可能意味着oracle认为全表扫描比使用索引更快。您是否在查询中使用了任何提示,可能会告诉oracle进行全面扫描?

您能否向我们提供完整的查询和解释计划结果?

编辑:亚伦,您可以使用“dbms_stats.gather_schema_stats”或“dbms_stats.gather_table_stats”命令更新统计信息。有关这些命令的更多信息,请参阅here。这将更新指定的模式或表的所有相关统计信息。 Oracle基于成本的优化器将使用统计信息来确定选择哪个执行计划。它从不使用实际的表格大小。当表格的大小发生显着变化时(+/- 10%左右),您需要重新更新统计信息。

另一件事。当您使用复合索引时,您需要指定查询中索引中使用的所有列,以便优化器考虑索引(我认为您需要按照相同的顺序指定它们,尽管我可能错了那已经有一段时间了,因为我看了这个东西)

+0

我编辑了我的问题。该索引应该是最新的,因为我刚刚删除并重新创建它。我如何检查统计信息? – 2009-10-16 09:25:24

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'); 

此外,究竟你是如何生成解释计划?如果启用跟踪,您是否有权访问数据库主机以检索跟踪文件?

正如我所评论的那样,查看查询的实际执行的跟踪会很好。既然你已经表明你可以访问数据库主机的文件系统,运行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”

  • 流程/轮廓跟踪文件 发出操作系统命令TKPROF traceFileName.trc tkprof.out

检查这个文件 - 你会看到一些开销信息,但会有一个个分组在详细的实际执行计划和查询统计。如果你看到这个信息相同的结果,则接下来的一步是添加另一份声明为什么在Oracle CBO是忽略了索引,将转储信息(第一“alter session的”后):

ALTER SESSION SET EVENTS='10053 trace name context forever, level 1'; 
+0

第一个查询返回107,640,377,第二个查询将该数字分成五个分区。最后一个返回索引112,153,440。 – 2009-10-16 13:01:23

+0

有一点可能会产生影响:我创建了比我需要的更多的分区(我已经在接下来的20年中创建了分区)。到目前为止这些都是空的(所以我有五个分区,行数大致相同,二十个分别为0行)。 – 2009-10-16 13:02:50

+0

这是一个好消息 - 看起来你可以使用基本的统计数据。正如我所提到的,我认为看到一个被跟踪的执行可能很好 - 你能访问数据库主机文件系统吗? – dpbradley 2009-10-16 13:05:30

1

我m并不是真正的分区专家,但我想这里发生的事情是,你已经创建了一个全局索引 - 一个包含所有分区中的行的索引。因此,优化器必须在两个互斥的访问路径之间进行选择:(A)索引范围扫描或(B)分区修剪。我相信PARTITION RANGE操作表示它已经选择了B.

正如其他人所建议的那样更新统计信息可能会改变行为。当您删除并重新创建索引时,将丢弃索引存在的所有统计信息。

将索引创建为UNIQUE(如果时间戳和键唯一标识一行)将是一个好主意,并且可能会改变行为。

但是,我认为真正的“修复”是您应该改为创建本地索引 - 每个分区上的单独索引。这应该使优化器能够进行分区修剪,然后进行索引查找。老实说,我不确定这样做的确切语法是什么。也许你只是使用单独的分区名称明确地在每个分区上创建索引。

+1

对于那些想了解更多信息的人,请查看http://download.oracle.com/docs/cd/B10501_01/server.920/a96524/c12parti.htm#459801。有关如何创建本地分区索引的示例,请搜索“本地索引创建的示例”。 – 2009-10-16 14:03:53

+0

基本上,在列列表之后添加单词“local”:'在演示(ts,键)上创建索引demo.x1 LOCAL' – 2009-10-16 14:19:21

+0

我已经重新创建索引,但EXPLAIN PLAN的结果不会更改。我检查了'USER_INDEXES'的内容,并且新的索引在'PARTITIONED'列中带有'YES'。这开始激怒我:) – 2009-10-16 14:21:04

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') 
+0

感谢提示.Oracle不会被打扰:(它固执地坚持全表扫描 – 2009-10-16 15:14:07

+0

但是当我添加第二个索引列(键)时,它突然生效! – 2009-10-16 15:15:34