我们有Oracle 10g,我们需要查询1个表(没有连接)并过滤掉其中1个列为空的行。当我们这样做 - WHERE OurColumn不是NULL - 我们在一张非常大的桌子上得到一个全表扫描 - BAD BAD BAD。该列上有一个索引但在此实例中会被忽略。这有什么解决方案吗?
由于
答案 0 :(得分:23)
优化器认为全表扫描会更好。
如果只有几个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%
所述,聚类因子是衡量此值的更准确方法,但{{1}}仍然是一个很好的经验法则。
答案 1 :(得分:16)
优化器将根据全表扫描的相对成本和使用索引做出决策。这主要归结为必须读取多少块以满足查询。在另一个答案中提到的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%的行具有非null 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行相比),并建议使用全表扫描。请参阅this DBAZine article on clustering factors。
答案 2 :(得分:2)
如果您正在执行select *,那么执行表扫描而不是使用索引是有意义的。如果您知道自己感兴趣的列,则可以使用这些列加上应用IS NOT NULL条件的列来创建覆盖索引。
答案 3 :(得分:1)
它可能取决于您在桌面上的索引类型。
大多数B树索引不存储空条目。位图索引执行存储空条目。
所以,如果你有:
从mytable中选择* mycolumn为null
并且您在mycolumn
上有一个标准的B树索引,然后查询不能使用索引,因为“null”不在索引中。
(如果索引针对多个列,并且其中一个索引列不为null,则索引中将有一个条目。)
答案 4 :(得分:0)
在该列上创建索引。
为了确保使用索引,它应该位于索引和where。
中的其他列ocdecio回答:
如果您正在执行select *,那么执行表扫描而不是使用索引是有意义的。
这不是严格正确的;如果存在适合您的where子句的索引,则将使用索引,并且查询优化器决定使用该索引比执行表扫描更快。如果没有索引或没有合适的索引,则必须进行表扫描。
答案 5 :(得分:0)
还有必要检查Oracle的桌面统计数据是否是最新的。它可能不知道全表扫描会更慢。
答案 6 :(得分:0)
Oracle数据库根本不在常规(b-tree)索引中索引空值,因此它不能使用它,也不能强制oracle数据库使用它。
BR
答案 7 :(得分:0)
使用提示只能作为解决方法而不是解决方案。
正如其他答案中所提到的,空值在B-TREE索引中不可用。
由于您知道此列中大部分都是空值,因此您是否可以将空值替换为范围。
这实际上取决于您的列和数据的性质,但通常情况下,如果您的列是日期类型,例如:
where mydatecolumn is not null
可以在规则中翻译说:我想要所有有日期的行。
然后你绝对可以这样做: 其中mydatecolumn< = sysdate(在oracle中)
这将返回所有带有日期和ommit null值的行,同时利用该列上的索引而不使用任何提示。
答案 8 :(得分:-1)
请参阅http://www.oracloid.com/2006/05/using-index-for-is-null/
如果您的索引位于一个字段上,则不会使用它。尝试在索引中添加虚拟字段或常量:
create index tind on t(field_to_index, 1);