Oracle SQL中的索引、解释计划和记录访问
我一直在学习Oracle SQL中的索引,我想用一个测试表进行一个小实验,看看索引是如何工作的。正如我在前面的一篇文章中所发现的,最好的方法是使用解释计划。然而,我遇到了一些让我困惑的事情 我的示例表包含属性(EmpID、Fname、Lname、occulation等等)。我使用自己编写的java程序(随机名称、职业等)将500000条记录填充到了它中。现在,以下是一些带索引和不带索引的示例查询: 无索引:Oracle SQL中的索引、解释计划和记录访问,sql,oracle,database-design,Sql,Oracle,Database Design,我一直在学习Oracle SQL中的索引,我想用一个测试表进行一个小实验,看看索引是如何工作的。正如我在前面的一篇文章中所发现的,最好的方法是使用解释计划。然而,我遇到了一些让我困惑的事情 我的示例表包含属性(EmpID、Fname、Lname、occulation等等)。我使用自己编写的java程序(随机名称、职业等)将500000条记录填充到了它中。现在,以下是一些带索引和不带索引的示例查询: 无索引: SELECT Fname FROM EMPLOYEE WHERE Occupation
SELECT Fname FROM EMPLOYEE WHERE Occupation = 'DOCTOR';
SELECT Fname FROM EMPLOYEE WHERE Occupation = 'DOCTOR';
解释计划说:
OPERATION OPTIMIZER COST
TABLE ACCESS(FULL) TEST.EMPLOYEE ANALYZED 1169
OPERATION OPTIMIZER COST
TABLE ACCESS(FULL) TEST.EMPLOYEE ANALYZED 1169
OPERATION OPTIMIZER COST
INDEX(RANGE SCAN) TEST.OCCUPATION_IDX ANALYZED 67
现在我创建索引:
CREATE INDEX occupation_idx
ON EMPLOYEE (Occupation);
索引为“职业”\u idx:
解释计划说:
OPERATION OPTIMIZER COST
TABLE ACCESS(FULL) TEST.EMPLOYEE ANALYZED 1169
OPERATION OPTIMIZER COST
TABLE ACCESS(FULL) TEST.EMPLOYEE ANALYZED 1169
OPERATION OPTIMIZER COST
INDEX(RANGE SCAN) TEST.OCCUPATION_IDX ANALYZED 67
所以。。。成本还是一样,1169?现在我试试这个:
索引为“职业”\u idx:
解释计划说:
OPERATION OPTIMIZER COST
TABLE ACCESS(FULL) TEST.EMPLOYEE ANALYZED 1169
OPERATION OPTIMIZER COST
TABLE ACCESS(FULL) TEST.EMPLOYEE ANALYZED 1169
OPERATION OPTIMIZER COST
INDEX(RANGE SCAN) TEST.OCCUPATION_IDX ANALYZED 67
因此,似乎只有当该列是我从中提取值的唯一列时,才会使用索引。但是我认为索引的目的是使用索引列作为键来解锁整个记录?上面的搜索毫无意义。。。它搜索您已经知道的值。我能想到的唯一有价值的查询,只涉及一个索引列的值(而不是记录的其余部分),应该是一个聚合,比如COUNT之类的
我遗漏了什么?即使有了您的索引,Oracle还是决定对第二个查询进行完整扫描 它为什么这样做?Oracle将创建两个计划,并为每个计划计算成本:- 1) 全扫描 2) 索引访问 Oracle选择了成本较低的计划。很明显,全扫描成本更低 如果您想查看索引计划的成本,可以使用如下提示执行解释计划以强制使用索引:
SELECT /*+ INDEX(EMPLOYEE occupation_idx) */ Fname
FROM EMPLOYEE WHERE Occupation = 'DOCTOR';
如果您在上面做一个解释计划,您将看到成本大于完整扫描成本。这就是为什么Oracle没有选择使用索引
P>一种考虑指标方案成本的简单方法是:-<
- 索引的b级别(从上到下必须读取多少块)
- 为在索引中匹配记录而必须随后读取的表块数。这取决于甲骨文公司对从事“医生”职业的员工数量的估计。在您的简单示例中,这将是: 行数/不同值的数量
SELECT COUNT(*), COUNT(DISTINCT( Occupation ))
FROM EMPLOYEE;
这将允许人们对索引计划的成本进行评论。作为WAG。分析表格和索引,然后查看计划是否更改
当您仅选择职业时,可以通过索引满足整个查询。该索引实际上有一份职业的副本。当您向select添加一个附加列时,Oracle必须转到数据记录以获取它。优化器选择读取所有数据行,而不是所有索引行和数据行。更便宜。索引是只存储以下数据的表的副本:
- 索引字段
- 指向原始行(
)的指针rowid
rowid id name occupation
[1] 1 John clerk
[2] 2 Jim manager
[3] 3 Jane boss
occupation rowid
boss [3]
manager [2]
clerk [1]
然后,职业
上的索引如下所示:
rowid id name occupation
[1] 1 John clerk
[2] 2 Jim manager
[3] 3 Jane boss
occupation rowid
boss [3]
manager [2]
clerk [1]
,记录按职业
在B树
中排序
如您所见,如果只选择索引字段,则只需要索引(第二个表)
如果您选择的不是职业
:
SELECT *
FROM mytable
WHERE occupation = 'clerk'
然后,引擎应该做两件事:首先在索引中查找相关记录,然后通过rowid
在原始表中查找记录。这就像您将rowid
上的两个表合并在一起一样
由于索引中的rowid不按顺序排列,因此对原始表的读取不是顺序的,可能会很慢。按顺序读取原始表并使用occulation='clerk'
筛选记录可能会更快
引擎不会“解锁”记录:它只是在索引中查找rowid,如果索引本身没有足够的数据,它会通过找到的
rowid
在原始表中查找数据。我想我看到了这里发生的事情
当索引就位后,您将执行以下操作:
SELECT Occupation FROM EMPLOYEE WHERE Occupation = 'DOCTOR';
执行计划将使用索引。这是一个很简单的问题,因为满足查询所需的所有数据都在索引中,Oracle甚至根本不需要引用表
但是,当您这样做时:
SELECT Fname FROM EMPLOYEE WHERE Occupation = 'DOCTOR';
然后,如果Oracle使用该索引,它将执行索引范围扫描,然后通过ROWID进行表访问,以查找与该职业对应的Fname。现在,根据占用了多少行,Oracle将不得不对表进行一次或多次访问,以查找Fname。例如,如果您有一个表,并且所有员工的职业都设置为“医生”,则索引没有多大用处,Oracle只需对该表进行完整的表扫描。如果有10000名员工,而且只有一名是医生,那么同样,这是一个不需要动脑筋的问题,甲骨文将使用该索引
但当你处于这两个极端之间时,你会发现一些微妙之处。在讨论是否使用索引时,人们喜欢谈论“选择性”,即索引标识了多少行,而不是表的大小。但事实并非如此。Oracle真正关心的是块选择性。也就是说,为了满足查询,它必须访问多少个块?那么,首先,距离扫描有多宽?范围越小