2011-11-17 50 views
5

我一直在学习Oracle SQL中的索引,并且我想用测试表进行一个小实验,以查看索引是如何工作的。正如我在此处发布的一篇文章中发现的,最好的方法是使用EXPLAIN PLAN。但是,我遇到了让我困惑的事情。索引,EXPLAIN PLAN和Oracle SQL中的记录访问

我的示例表包含属性(EmpID,Fname,Lname,Occupation,....等)。我使用我写的java程序(随机名称,职业等)填充了500,000条记录。现在,这里有一些示例查询使用和不使用索引:

NO INDEX:

SELECT Fname FROM EMPLOYEE WHERE Occupation = 'DOCTOR'; 

EXPLAIN PLAN说:

OPERATION       OPTIMIZER COST 
TABLE ACCESS(FULL) TEST.EMPLOYEE ANALYZED 1169 

现在我创建索引:

CREATE INDEX occupation_idx 
    ON EMPLOYEE (Occupation); 

WITH INDEX“occupation_idx”:

SELECT Fname FROM EMPLOYEE WHERE Occupation = 'DOCTOR'; 

EXPLAIN PLAN说:

OPERATION       OPTIMIZER COST 
TABLE ACCESS(FULL) TEST.EMPLOYEE ANALYZED 1169 

所以...成本还是一样,1169?我现在试试这个:

WITH INDEX “occupation_idx”:

SELECT Occupation FROM EMPLOYEE WHERE Occupation = 'DOCTOR'; 

EXPLAIN PLAN说:

OPERATION        OPTIMIZER COST 
INDEX(RANGE SCAN) TEST.OCCUPATION_IDX ANALYZED 67 

所以,看来当该列是唯一一个索引只使用我从中拉取价值。但我认为索引的要点是使用索引列作为关键字解锁整个记录?上面的搜索是一个毫无意义的搜索...它搜索你已经知道的值。我能想到的唯一有价值的查询只涉及索引列的值(而不是记录的其余部分),它将是诸如COUNT之类的集合。

我错过了什么?

+0

有趣的问题,我会好奇,看看为什么会发生这种情况。 – ChandlerPelhams

+0

是为EMPLOYEE表定义的主键吗? –

+0

索引是否可能尚未完成?如果您重新运行查询SELECT FNAME FROM EMPLOYEE WHERE Occupation ='DOCTOR';它的成本下降了吗? – xQbert

回答

5

即使使用您的索引,Oracle也决定对第二个查询进行全面扫描。

它为什么这样做?甲骨文将创建了两个计划,并拿出为每个成本: -

1)全扫描

2)索引访问

甲骨文选择具有较低成本的计划。很显然,它将全面扫描作为较低的成本。

如果你想看到的指标计划的成本,你可以做一个像这样带有一丝的解释计划来强制使用索引:

SELECT /*+ INDEX(EMPLOYEE occupation_idx) */ Fname 
FROM EMPLOYEE WHERE Occupation = 'DOCTOR'; 

如果一个以上解释计划,您会看到成本高于全扫描成本。这就是为什么Oracle没有选择使用索引。

一个简单的方法来考虑指标计划的成本是: -

  • 指数(多少块必须从上到下读取)
  • 表块数的blevel必须随后读取索引中匹配的记录。这依赖于甲骨文对拥有“医生”职位的员工人数的估计。在简单的例子中,这将是:不同值的

    行数/数

更复杂的考虑因素包括聚类工厂和索引费用调整这两者反映所读取的块的情形产生已经在内存中,因此不需要从磁盘读取。

也许你可以从你的查询的结果与索引提示,也该查询的结果更新你的问题: -

SELECT COUNT(*), COUNT(DISTINCT(Occupation)) 
FROM EMPLOYEE; 

这将使人们对指数计划的成本发表评论。

+0

感谢您的回复。我从来不知道“提示”。但是,它似乎并没有工作。即使有了这个提示,它仍然会进行全面扫描,就像没有提示一样。 – The111

+0

@约翰逊:我有错误的提示。请参阅编辑。 –

+0

感谢您的修正提示。当我创建我的表格时,我故意让我的随机器脚本对每个字段都有不同的大小域。例如,lname字段从500个lnames中选择,而fname字段仅从100个中选择。我知道这些变体最后很有趣。使用提示,我现在能够精确地指出索引变得有用的大小域(顺便说一下,当域有大约450个值时可能......或换句话说:当索引帐户找到的行不到表格的0.2%)。 – The111

0

作为一个WAG。分析表格和索引,然后查看计划是否更改。

当您仅选择职业时,可以从索引中满足整个查询。索引字面上有一份职业的副本。在您为select添加额外列的那一刻,Oracle必须转到数据记录才能得到它。优化器选择读取所有数据行而不是所有索引行和数据行。这个更便宜。

+0

没有运气。不过谢谢。 – The111

+0

那么,全表扫描是更便宜的方式来做到这一点。我们在这里讨论的是什么样的数据量? – EvilTeach

+0

给你的箭袋添加另一个箭头。学习使用AUTOTRACE和SQLPLUS。 http://www.adp-gmbh.ch/ora/sqlplus/autotrace.html – EvilTeach

2

索引是其中仅存储以下数据中的表的副本:

  • 索引字段(一个或多个)
  • 的指针原始行(rowid)。

假设你有一个表是这样的:

rowid id name occupation 
[1]  1 John clerk 
[2]  2 Jim manager 
[3]  3 Jane boss 

然后在occupation的指数应该是这样的:

occupation rowid 
boss  [3] 
manager  [2] 
clerk  [1] 

,与记录在B-Tree上排序occupation。如你所见,如果你只选择索引字段,你只需要索引(第二个表)。

如果选择除occupation其他任何东西:

SELECT * 
FROM mytable 
WHERE occupation = 'clerk' 

此时发动机应该让两件事情:首先找到在索引中的相关记录,第二,通过rowid发现在原表中的记录。这就像是你加入了rowid的两张桌子。

由于索引中的rowid未按顺序排列,因此对原始表的读取不是顺序的,可能会很慢。按顺序读取原始表可能会更快,只需使用occupation = 'clerk'过滤记录即可。

引擎不“解锁”记录:它只是在索引中找到rowid,并且如果索引本身没有足够的数据,它将通过找到的rowid查找原始表中的数据。

3

我想我看到这里发生了什么。

当您准备好索引,你这样做:

SELECT Occupation FROM EMPLOYEE WHERE Occupation = 'DOCTOR'; 

执行计划将使用索引。这是一件容易的事情,因为索引中所有满足查询要求的数据都在索引中,Oracle甚至根本无需引用表。

但是,当你这样做:

SELECT Fname FROM EMPLOYEE WHERE Occupation = 'DOCTOR'; 

那么,如果Oracle使用的指数,它会做一个索引范围扫描,然后一个TABLE ACCESS BY ROWID查找对应于该职业FName参数。现在,根据具有DOCTOR for Occupation的行数,Oracle必须进行一次或多次到表的查找,以查找Fname。例如,如果您有一个表格,并且所有员工都将“职业”设置为“DOCTOR”,那么索引用处不大,Oracle将仅执行表格的FULL TABLE SCAN。如果有10,000名员工,并且只有一名是DOCTOR,那么再说一次,这是毫不费力的,Oracle将使用该索引。

但有一些微妙之处,当你在这两个极端之间的某处。在讨论是否使用索引时,人们喜欢谈论“选择性”,即索引标识了多少行,与表的大小。但是,这不是真的是真的。什么Oracle 真的关心的是块选择性。也就是说,有多少区块是否必须访问以满足查询?所以,首先,RANGE SCAN的“宽”是多少?谓词值指定的值范围越有限,越好。其次,当您的查询需要执行表查找时,需要访问多少个不同的块才能找到所需的所有数据。也就是说,相对于索引顺序,中的数据如何“随机”?这被称为CLUSTERING_FACTOR。如果您分析索引以收集统计信息,然后查看USER_INDEXES,则会看到现在已经填充了CLUSTERING_FACTOR。

那么,什么是CLUSTERING_FACTOR? CLUSTERING_FACTOR是关于索引的关键列的表的“有序性”。 CLUSTERING_FACTOR的值将始终位于表中的块数和表中的行数之间。 A low CLUSTERING_FACTOR,即非常接近区块中的一个,表示相对于索引非常有序的表格。 A CLUSTERING_FACTOR,即表中非常接近的一个,相对于索引非常无序。

理解CLUSTERING_FACTOR描述中的数据相对于索引的顺序是一个重要概念。所以,重建一个索引,例如而不是更改CLUSTERING_FACTOR。了解同一个表可以有两个索引,一个可以有一个出色的CLUSTERING_FACTOR,另一个可能有一个非常差的CLUSTERING_FACTOR也很重要。表格本身只能以一种方式订购。

那么,为什么我花了这么多时间来描述CLUSTERING_FACTOR?因为当你有一个执行计划执行INDEX RANGE SCAN和TABLE ACCESS BY ROWID时,你可以确定Oracle的优化器已经考虑CLUSTERING_FACTOR来提出执行计划。例如,假设您有一个10,000行的表,并且假设有100个行拥有Occupation ='DOCTOR'。你写上面的查询,询问职业是医生的雇员的Fname。那么,Oracle可以非常轻松高效地确定职业是DOCTOR的行的rowid。但是,Oracle需要访问多少块块才能执行Fname查找?如果数据按表中的职业进行聚类(排序),则可能只有1个或2个表格块。但是,如果数据在表格中非常无序,则可能多达100个!因此,我们再次假设10,000行表,并且假设(出于说明和简单的数学目的)该表具有100行/块,以及100个块。根据表格顺序(即CLUSTERING_FACTOR),表格访问次数可能少至1次,或多达100次。

所以,我希望这可以帮助您理解为什么优化器可能不愿意使用索引在某些情况下。

+0

+1有用的细节! –