oracle访问表中数据的方式笔记

访问表中数据的方式

1.全表扫描

全表扫描是指 Oracle 在访问目标表里的数据时,会从该表所占用的第一个区(EXTENT)的第一个块BLOCK)开始扫描,一直扫描到该表的高水位线(HWM,High Water Mark),这段范围内所有的数据块 Oracle都必须读到。当然,Oracle 会对这期间读到的所有数据施加目标 SOL 的 where 条件中指定的过滤条件,最后只返回那些满足过滤条件的数据。

不是说全表扫描不好,事实上 Oracle 在做全表扫描操作时会使用多块读,这在目标表的数据量不大时执行效率是非常高的,但全表扫描最大的问题就在于走全表扫描的目标 SQL 的执行时间会不稳定、不可控,这个执行时间一定会随着目标表数据量的递增而递增。因为随着目标表数据量的递增,它的高水位线会一直不断往上涨,所以全表扫描该表时所需要读取的数据块的数量也会不断增加,这意味着全表扫描该表时所需要耗费的 I/O资源会随之不断增加,当然完成对该表的全表扫描操作所需要耗费的时间也会随之增加。另外,对于CBO而言,所要耗费的 I/O 资源不断增加则意味着全表扫描的成本值也会随着目标表数据量的递增而递增。

在 Oracle 中,如果对目标表不停地插入数据,当分配给该表的现有空间不足时高水位线就会向上移动,但如果你用 DELETE 语句从该表删除数据,则高水位线并不会随之往下移动(这在某种程度上契合了“高水位线”的定义,就好比水库的水位,当水库涨水时,水位会往上移,当水库放水后,曾经的最高水位的痕迹还是会清晰可见)。高水位线的这种特性所带来的副作用是,即使使用 DELETE 语句删光了目标表中的所有数据高水位线还是会在原来的位置,这意味着全表扫描该表时 Oracle 还是需要扫描该表高水位线下的所有数据块所以此时对该表的全表扫描操作所耗费的时间与之前相比并不会有明显的改观。

2.rowid扫描

ROWID 扫描是指 Oracle 在访问目标表里的数据时,直接通过数据所在的ROWID去定位并访问这些数据ROWID表示的是 Oracle 中的数据行记录所在的物理存储地址,也就是说 ROWID 实际上是和 Oracle 中数据块里的行记录一一对应的。

既然 ROWID 代表的就是表的数据行所在的物理存储地址,那么当 Oracle 知道待访问的数据行所在的ROWID 后,自然就可以根据该 ROWID 去直接访问对应表的相关数据行,这就是 ROWID 扫描的含义。

从严格意义上来说,Oracle中的ROWID扫描有两层含:一种是根据用户在SOL语中输入的ROWID的值直接去访问对应的数据行记录:另外一种是先去访问相关的索引,然后根据访问索引后得到的 ROWID 再回表去访问对应的数据行记录。

3.oracle中常见的访问B数据索引的方法

3.1索引唯一性扫描(INDEX UNIOUE SCAN)

针对唯一性索引(UNIQUEINDEX)的扫描它仅仅适用于 where 条件里是等值查询的目标 SQL。因为扫描的对象是唯一性索引,所以索引唯一性扫描的结果至多只会返回一条记录。

3.2 索引范围扫描(INDEX RANGE SCAN)

适用于所有类型的 B 树索引,当扫描的对象是唯一性索引时,此时目标 SOL的 where条件一定是范围查询(谓词条件为 BETWEEN、<等)当扫的对象是非唯一性索引时,对目标 SOL的 where 条件没有限制(可以是等值查询,也可以是范围查询)。索引范围扫描的结果可能会返回多条记录,其实这就是索引范围扫描中“范围”二字的本质含义。
在同等条件下,当目标索引的索引行的数量大于1时,索引范围扫描所耗费的逻辑读至少会比相应的索引唯一性扫描的逻辑读多1。

3.3索引全扫描(INDEX FULL SCAN)

适用于所有类型的B 索引(包括唯一性索引和非唯一性索引)所谓的“索引全扫描”,就是指要扫描目标索引所有叶子块的所有索引行。这里需要注意的是,索引全扫描需要扫描目标索引的所有叶子块,但这并不意味着需要扫描该索引的所有分支块。在默认情况下,Oracle 在做索引全扫描时只需要通过访问必要的分支块定位到位于该索引最左边的叶子块的第一行索引行,就可以利用该索引叶子块之间的双向指针链表,从左至右依次顺序扫描该索引所有叶子块的所有索引行了。
既然在默认情况下,索引全扫描要从左至右依次顺序扫描目标索引所有叶子块的所有索引行,而索引是有序的,所以索引全扫描的执行结果也是有序的,并且是按照该索引的索引键值列来排序,这也意味着走索引全扫描能够既达到排序的效果,又同时避免了对该索引的索引键值列的真正排序操作。

默认情况下,索引全扫描的扫描结果的有序性就决定了索引全扫描是不能够并行执行的,并且通常情况下索引全扫描使用的是单块读。

通常情况下,索引全扫描是不需要回表的,所以索引全扫描适用于目标 SOL 的查询列全部是目标索引的索引键值列的情形。我们知道,对于 Oracle 数据库中的 B 树索引而言,当所有索引键值列全为 NULL 值时不入索引(即当所有索引键值列全为 NULL 值时,这些 NULL值不会在 B 索引中存在),这意味着Oracle 中能做索引全扫描的前提条件是目标索引至少有一个索引键值列的属性是 NOT NULL。这是很显然的事情,如果目标索引的所有索引键值列的属性均为允许 NULL 值,此时如果还走索引全扫描,就会漏掉目标表中那些索引键值列均为 NULL 的记录,即此时走索引全扫描的结果就不准了!Oracle 显然不会允许这种事情发生

3.4 索引快速全扫描(INDEX FASTFULLSCAN)

索引快速全扫描(INDEX FASTFULLSCAN)和索引全扫描极为类似,它也适用于所有类型的B 树索引(包括唯一性索引和非唯一性索引)。和索引全扫描一样,索引快速全扫描也需要扫描目标索引所有叶子块的所
有索引行。
索引快速全扫描与索引全扫描相比有如下三点区别。
(1)索引快速全扫描只适用于CBO
(2)索引快速全扫描可以使用多块读,也可以并行执行。
(3)索引快速全扫描的执行结果不一定是有序的。这是因为索引快速全扫描时Oracle 是根据索引行在磁盘上的物理存储顺序来扫描,而不是根据索引行的逻辑顺序来扫描的,所以扫描结果才不一定有序(对于单个索引叶子块中的索引行而言,其物理存储顺序和逻辑存储顺序一致:但对于物理存储位置相邻的索引叶子块而言,块与块之间索引行的物理存储顺序则不一定在逻辑上有序)。

执行如下带Hint 的目标SQL(这里/+index ffs(emp_test pk emp_test)/的目的是让Oracle 走对主键索引PKEMPTEST的索引快速全扫描):

SQL> select /*+ index ffs(emp test pk emp test) */empno from emp test;

3.5 索引跳跃式扫描(INDEXSKIPSCAN)

适用于所有类型的复合 B 索引(包括唯一性索引和非唯一性索引),它使那些在 where 条件中没有对目标索引的前导列指定查询条件但同时又对该索引的非前导列指定了查询条件的目标 SOL 依然可以用上该索引,这就像是在扫描该索引时跳过了它的前导列,直接从该索引的非前导列开始扫描一样(实际的执行过程并非如此),这也是索引跳跃式扫中“跳跃”(SKIP)一词的含义。
为什么在 where 条件中没有对目标索引的前导列指定查询条件 Oracle 依然可以用上该索引呢?这是因为Oracle帮你对该索引的前导列的所有distinct 值做了遍历。
所谓的对目标索引的所有 distinct 值做遍历,其实际含义相当于对原目标SOL 做等价改写(即把要用的目标索引的所有前导列的 distinct值都加进来)索引IDX EMPOLYEE 的前导列GENDER 的 distinct 值只有“F”和“M”两个值,所以这里能使用索引IDX_EMPOLYEE 的原因可以简单地理解成是 Oracle 将范例SOL9等价改写成了如下形式:

SQL> select * from employee where employee id = 100;
-- 改写之后
select * from employee where gender = 'F'and employee id = 100
union all
select * from employee where gender = !M' and employee id = 100;

从上述分析过程可以看出,Oracle 中的索引跳跃式扫描仅仅适用于那些目标索引前导列的 distinct 值数量较少、后续非前导列的可选择性又非常好的情形,因为索引跳跃式扫描的执行效率一定会随着目标索引前导列的distinct 值数量的递增而递减。

你可能感兴趣的:(数据库,sql)