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

没有提到指数。有什么问题?

[编辑] 出于某种原因,Oracle 完全错误地计算了该操作的成本。我在该表中有 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] 找到它;请参阅下面的答案。

4

5 回答 5

5

好的,这是我的一个错误:该列的类型是DATE,而不是TIMESTAMP。由于我使用过to_timestamp(),Oracle 看不到使用索引的方法。

于 2009-10-20T13:37:15.993 回答
1

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

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

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

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

于 2009-10-15T16:11:28.267 回答
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');

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

[编辑] 正如我所评论的,最好能看到查询实际执行的痕迹。由于您已表明您可以访问 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';

  • 使用任何可用的搜索工具来查找包含字符串“THIS IS THE TRACE”的文件

  • 通过发出 OS 命令tkprof traceFileName.trc tkprof.out处理/分析跟踪文件

检查这个文件——你会看到一些开销信息,但是会有一个部分详细说明查询的实际执行计划和统计信息。如果您在此信息中看到相同的结果,那么下一步是添加另一个语句(在第一个“alter session”之后),它将转储有关 Oracle CBO 忽略索引的原因的信息:

ALTER SESSION SET EVENTS='10053 trace name context forever, level 1';
于 2009-10-16T12:21:38.463 回答
1

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

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

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

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

于 2009-10-16T13:54:38.373 回答
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')
于 2009-10-16T14:03:37.013 回答