Oracle 10g - 优化 WHERE IS NOT NULL

问题描述 投票:0回答:9

我们有 Oracle 10g,我们需要查询一个表(没有

JOIN
)并过滤掉其中 1 列为空的行。当我们这样做时 -
WHERE OurColumn IS NOT NULL
- 我们在一个非常大的表上进行全表扫描 - 坏坏坏。该列上有一个索引,但在这种情况下它会被忽略。有没有办法解决这个问题?

谢谢

oracle optimization null
9个回答
27
投票

优化器认为全表扫描会更好。

如果只有几行

NULL
,则优化器是正确的。

如果您绝对确定索引访问会更快(也就是说,您有超过

75%
行且带有
col1 IS NULL
),则提示您的查询:

SELECT  /*+ INDEX (t index_name_on_col1) */
        *
FROM    mytable t
WHERE   col1 IS NOT NULL

为什么

75%

因为使用

INDEX SCAN
检索索引未覆盖的值意味着
ROWID
上的隐藏联接,其成本约为表扫描的
4
倍。

如果索引范围包含超过

25%
行,表扫描通常会更快。

正如

Tony Andrews
所提到的,聚类因子是衡量该值的更准确方法,但
25%
仍然是一个很好的经验法则。


17
投票

优化器将根据全表扫描和使用索引的相对成本做出决定。这主要取决于必须读取多少块才能满足查询。另一个答案中提到的 25%/75% 经验法则很简单:在某些情况下,即使获取 1% 的行,全表扫描也是有意义的 - 即,如果这些行碰巧分布在许多块上。

例如,考虑这张表:

SQL> create table t1 as select object_id, object_name from all_objects;

Table created.
SQL> alter table t1 modify object_id null;

Table altered.

SQL> update t1 set object_id = null
  2  where mod(object_id,100) != 0
  3  /

84558 rows updated.

SQL> analyze table t1 compute statistics;

Table analyzed.

SQL> select count(*) from t1 where object_id is not null;

  COUNT(*)
----------
       861    

如您所见,T1 中只有大约 1% 的行具有非空 object_id。但由于我构建表的方式,这 861 行将或多或少均匀地分布在表周围。因此,查询:

select * from t1 where object_id is not null;

可能会访问 T1 中的几乎每个块来获取数据,即使优化器使用了索引。那么放弃索引并进行全表扫描是有意义的!

帮助识别这种情况的一个关键统计数据是索引聚类因子:

SQL> select clustering_factor from user_indexes where index_name='T1_IDX';

CLUSTERING_FACTOR
-----------------
              460

这个值 460 相当高(与索引中的 861 行相比),表明将使用全表扫描。请参阅 这篇关于聚类因素的 DBAZine 文章


3
投票

如果您正在进行 select *,那么进行表扫描而不是使用索引是有意义的。如果您知道对哪些列感兴趣,则可以使用这些列加上您要应用 IS NOT NULL 条件的列来创建覆盖索引。


1
投票

这可能取决于表上索引的类型。

大多数 B 树索引存储空条目。位图索引 do 存储空条目。

所以,如果你有:

从我的表中选择* 其中 mycolumn 为空

并且您在

mycolumn
上有一个标准 B 树索引,那么查询 can't 使用该索引,因为“null”不在索引中。

(如果索引针对多个列,并且索引列之一不为空,则索引中将会有一个条目。)


0
投票

在该列上创建索引。

为了确保索引被使用,它应该位于索引和where中的其他列上。

ocdecio 回答:

如果您正在执行 select *,那么进行表扫描而不是使用索引会更有意义。

这并不完全正确;如果存在适合您的 where 子句的索引,并且查询优化器决定使用该索引比执行表扫描更快,则将使用索引。如果没有索引,或者没有合适的索引,才必须进行表扫描。


0
投票

还值得检查 Oracle 表上的统计信息是否是最新的。它可能不知道全表扫描会更慢。


0
投票

Oracle 数据库根本不在常规(b 树)索引中索引空值,因此它不能使用它,您也不能强制 Oracle 数据库使用它。

BR


0
投票

使用提示只能作为解决方法,而不是解决方案。

正如其他答案中提到的,空值在 B-TREE 索引中不可用。

既然您知道此列中大部分为空值,那么您是否能够用一个范围来替换空值。

这实际上取决于您的列和数据的性质,但通常情况下,例如,如果您的列是日期类型:

where mydatecolumn is not null
可以翻译成一条规则:我想要所有有日期的行。

那么你绝对可以这样做: 我的日期列在哪里 <=sysdate (in oracle)

这将返回带有日期的所有行并忽略空值,同时利用该列上的索引而不使用任何提示。


-1
投票

参见 http://www.oracloid.com/2006/05/using-index-for-is-null/

如果您的索引位于一个字段上,则不会使用该索引。尝试在索引中添加虚拟字段或常量:

create index tind on t(field_to_index, 1); 
© www.soinside.com 2019 - 2024. All rights reserved.