我的测试数据库中有一个包含25万行的表。(有几亿个产品正在生产,我们在那里也可以观察到相同的问题。)该表具有nvarchar2(50)字符串标识符(不是null),并且具有唯一索引(不是PK)。
标识符由第一部分组成,该部分在我的测试数据库中具有8个不同的值(在生产中大约有1000个),然后是@符号,最后是一个1到6位数字的数字。例如,可能有5万行以“ ABCD_BGX1741F_2006_13_20110808.xml @”开头,然后是5万个不同的数字。
当我根据其标识符查询单行时,基数估计为1,成本非常低,可以正常工作。当我在一个IN表达式或一个OR表达式中查询带有多个标识符的多行时,索引的估计是完全错误的,因此使用了全表扫描。如果我用一个索引强加索引,它会非常快,实际上全表扫描的执行速度要慢一个数量级(并且生产速度要慢得多)。因此,这是一个优化程序问题。
作为测试,我使用完全相同的DDL和完全相同的内容复制了该表(在相同的模式+表空间中)。我在第一个表上重新创建了唯一索引以取得良好效果,并在克隆表上创建了完全相同的索引。我做了一个DBMS_STATS.GATHER_SCHEMA_STATS('schemaname',estimate_percent=>100,cascade=>true);
。您甚至可以看到索引名称是连续的。因此,现在两个表之间的唯一区别是,第一个表是在很长一段时间内以随机顺序加载的,块分散在磁盘上(与其他几个大表一起放在一个表空间中),第二个表是成批加载的插入选择。除此之外,我无法想象任何区别。(自上次大删除以来,原始表已缩小,此后没有一个删除。)
这是病假表和克隆表的查询计划(黑色笔刷下的字符串在图片中相同,灰色笔刷下的字符串也一样):
(在此示例中,有1867行以黑色刷过的标识符开头。2行查询产生的基数为1867 * 2,3行查询产生的基数为1867 * 3,依此类推。巧合的是,Oracle似乎并不在乎标识符的结尾。)
什么可能导致此行为?显然,在生产中重新创建表将非常昂贵。
USER_TABLES:http : //i.stack.imgur.com/nDWze.jpg USER_INDEXES:http : //i.stack.imgur.com/DG9um.jpg 我只更改了架构和表空间名称。您可以看到表名和索引名与查询计划屏幕快照上的相同。