4

我有这张桌子:

create table demo (
    key number(10) not null,
    type varchar2(3) not null,
    state varchar2(16) not null,
    ... lots more columns ...
)

这个索引:

create index demo_x04 on demo(key, type, state);

当我运行这个查询

select * from demo where key = 1 and type = '003' and state = 'NEW'

EXPLAIN PLAN表明它进行了全表扫描。所以我删除了索引并再次创建它。EXPLAIN PLAN仍然说全表扫描。怎么可能?

一些背景:这是历史数据,所以发生的事情是我查找带有状态的行CLEARED并插入带有状态的新行NEW(另外我从旧行中复制了一些值)。然后将旧行更新为USED. 所以桌子总是在变大。我确实注意到索引的基数为 0(尽管我有数千个不同的值)。重新创建后,基数增加了,但 CBO 并不喜欢该指数。

第二天早上,Oracle 突然喜欢上了这个索引(可能是睡着了)并开始使用它,但时间不长。过了一会儿,处理速度从 50 行/秒下降到 3 行/秒,我再次看到“FULL TABLE SCAN”。到底是怎么回事?

就我而言,我需要处理大约一百万行。我分批提交更改。50. 在提交更新/重组索引或类似的东西后,我应该运行一些命令吗?

我在 Oracle 10g 上。

[编辑] 我在这张表中有 969'491 个不同的键,3 种类型和 3 种状态。

4

5 回答 5

4

如果指定索引提示会发生什么?试试这个:

SELECT /*+ INDEX (demo demo_x04) */ * 
  FROM demo 
 WHERE key = 1 
   AND type = '003' 
   AND state = 'NEW';

听起来一夜之间发生的事情是对表格进行了分析。然后,当您对表运行处理时,更新了足够多的索引以导致 oracle 表的统计信息再次失效,并且优化器停止使用该索引。

添加提示并查看 EXPLAIN PLAN 是否为您提供不同的计划并且查询执行得更好。

哦,Tony 关于分析表的回答是一个普遍的好习惯,尽管使用 10g 的数据库在这方面进行自我维护非常好。如果您的进程正在进行大量更新,则索引可能会很快过时。如果在您的进程开始陷入困境时运行分析可以暂时改善情况,那么您就会知道这是问题所在。

要更新表的统计信息,请使用dmbs_stats.gather_table_stats包。

例如:

exec dbms_stats.gather_table_stats('所有者','DEMO');

于 2009-11-14T15:29:32.203 回答
3

最近有分析过表格吗?如果 Oracle 认为它很小,它甚至可能不会考虑使用索引。

试试这个:

select last_analyzed, num_rows 
from user_tables
where table_name = 'DEMO';

NUM_ROWS 告诉您 Oracle 认为该表包含多少行。

于 2009-11-14T15:00:34.227 回答
3

“第二天早上,Oracle 突然喜欢上了这个索引(可能是睡过了)” 可能是一个 DBMS_STATS 在一夜之间运行。

一般来说,我会看到对索引进行 FULL TABLE SCAN 的三个原因之一。首先是优化器认为表是空的,或者至少非常小。我怀疑这是最初的问题。在这种情况下,完全扫描仅包含少数块的表会比使用索引更快。

第二个是当查询使得索引无法实际使用时。

"select * from demo where key = 1 and type = '003' and state = 'NEW'"

您实际上是否在查询中使用硬编码的文字。如果不是,您的变量数据类型可能不正确(例如,键是字符)。这将需要将数字键转换为字符以进行比较,这将使索引几乎无用。

第三个原因是它认为查询将处理表中大部分行。类型和状态似乎相当低的基数。您是否可能拥有大量特定的“关键”值?

于 2009-11-15T10:16:50.313 回答
1

对您描述的处理的评论:听起来您正在通过间歇性提交进行逐行处理,如果可以的话,我敦促您重新考虑这一点。更新/插入机制很可能会转换为 MERGE 语句,然后整个数据集可以在一条语句中处理,最后一次提交。这几乎肯定会比您当前的方法更快并且使用更少的资源。

于 2009-11-16T06:02:12.150 回答
0

列键的值是否始终为 1?如果是这样,我不确定查阅索引是否会优化查询,因为无论如何都必须检查每一行。如果是这样,请声明没有键列的索引。你也可以试试:

select key, type, state from demo where key = 1 and type = '003' and state = 'NEW'

哪个(如果我的猜测是正确的)仍然需要查看每一行,但是哪个可能会进入索引,因为结果集中的所有列现在都被覆盖了。

我只是根据您的陈述猜测索引显示基数为 0。

于 2009-11-14T14:35:43.903 回答