Oracle sql优化必知――表的访问

《访问数据的方法

访问表中的数据有两种:1、直接访问表 2、先访问索引,再回表


1、直接访问表的两种方法

①、全表扫描

全表扫描是指Oracle在访问目标表的数据时,会从该表所占用的第一个区(extent)的第一个块(block)开始扫描,一直扫描到该表的高水位线,这段范围内的所有数据库都必须读到,当然如果目标sql的where中指定的过滤条件,最后只返回满足条件的数据即可;(有时候全表扫描的效率还是非常高的,但是随着表的数据增多 资源消耗也会在逐步增加


②、rowid扫描

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

Oracle中的rowid扫描有两层含义:

一种是根据用户sql语句中输入的rowid的值直接访问对应的数据行记录;

另一种是先去访问相关的索引,然后根据访问索引后得到的rowid再回表去访问对应的数据行记录。


2、访问索引的方法

常用的是B树索引,优点如下:

①:所有的索引叶子块都在同一层,即他们距离索引根节点的深度是相同的,这也意味着访问索引叶子块的任何一个索引键值所花费的时间几乎相同。

②:Oracle会保证所有的B树索引都是自平衡的,即不可能出现不同的索引叶子块不处同一层的现象。

③:通过B树索引访问表里行记录的效率并不会随着相关表的数据量递增而显著降低,即通过走索引访问数据的时间是可控的,基本稳定的,这也是走索引和全表扫描的最大区别;

一些常见的访问B树索引的方法

①:索引唯一性扫描(index unique scan):

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

②:索引范围扫描(index range scan)

索引范围扫描适用于所有类型的B树索引,当扫描的对象是唯一性索引时,此时目标sql的where条件一定是范围查询;要注意即使是针对同等条件下的相同的sql,当目标索引的数量大于1时,索引范围扫描所耗费的逻辑读会多于索引唯一性扫描所耗费的逻辑读。

③:索引全扫描(index full scan)

索引全扫描适用于所有类型的B树索引(包括唯一性索引和非唯一性索引)。是指要扫描目标索引所有叶子块的所有索引行。

认情况下,Oracle在做索引全扫描时只需要通过访问必要的分支块定位到位于该索引最左边的叶子块的第一行索引行;

说明:索引全扫描的执行结果也是有序的,并且是按照该索引的索引键值列来排序,这也意味着走索引全扫描能够即达到排序的

效果,又同时避免了该索引的索引键值列达的真正排序操作。

④:索引快速全扫描(index fast full scan)

索引快速全扫描和索引全扫描极为类似,它也是由于所有类型的B树索引(包括唯一性索引和非唯一性索引)。和索引全扫描一样,索引快速全扫描也需要扫描目标索引所有叶子块的所有索引行;

索引快速全扫描与索引全扫描的区别如下:

①:索引快速全扫描只适用CBO

②:索引快速全扫描可以使用多块读,也可以并行执行

③:索引快速全扫描的执行结果不一定是有序的。

例句:如下带hint的目标sql是让Oracle走对主键索引pk_emp_test的索引快速全扫描

(emp_test是表名 pk_emp_test是表的主键复合索引 empno是查询的字段)

select /*+ index_ffs(emp_test pk_emp_test) */empno from emp_test;

⑤:索引跳跃式扫描(index skip scan)

索引跳跃式扫描适用所有类型的复合B树索引(包括唯一性索引和非唯一性索引),它使那些在where条件中没有对目标索引的前导列指定查询条件

但同时对该索引的非前导列指定了查询条件的sql依然可以用上该索引。

注意:

Oracle中的索引跳跃式扫描仅适用于那些目标索引前导列的distinct值数量较少、后续非前导列的可选择性又非常好的图形,因为索引跳跃式扫描的执行

效率一定会随着目标索引前导列的distinct值数量的递增而递减。


清空数据字典缓:--生产库禁用

sql> alter system flush shared_pool;

清空buffer cache缓存:---生产库禁用

sql> alter system flush buffer_cache;


《表连接》

1、表连接顺序

不管目标sql有多少个表做表连接,Oracle在实际执行该sql时都只能先两两做表连接,再一次执行这样的两两表连接过程,直到目标sql中所有的表都已经连接完毕;

2、表连接方法

两个表连接的方法有:排序合并连接、嵌套循环连接、哈希连接、笛卡尔积连接 四种;


3、表连接的类型:

3.1内连接:

内连接是指表连接的连接结果只包含那些完全满足连接条件的记录。对于包含表连接的目标sql而言,只要起where条件中没有写那些标准sql中定义或者Oracle中自定义的表示外连接的关键字(比如标准sql中的left outer join、right outer join、full outer join,或者Oracle中自定义的用来表示外连接的关键字“+”),则该sql的连接类型就是内连接。

如: select t1.col1,t1.col2,t2.col3 from t1,t2 where t1.col2=t2.col2; 这条sql就没有那些关键字,这就是内连接

标准sql中的内连接写法是用:join on 或 join using (natural join 特殊的连接方法

join on 语法:目标表1 join 目标表2 on (连接条件)

如:select t1.col1,t2.col3 from t1 join t2 on (t1.col2=t2.col2);

join using 语法:目标表1 join 目标表2 using (连接列集合)

如:select t1.col1,col2,t2.col3 from t1 join t2 using (col2);

注意:对于使用join using的标准sql而言,如果连接列同时又出现在查询列中,则该连接列前不能带上表名或者表名的别名,否则Oracle会报错(ORA-25154)

natural join语法:目标表1 natural join 目标表2

如:select t1.col1,t2.col3 from t1 natural join t2;

注意:

对于内连接而言,除了表连接条件之外的额外限制条件在目标sqlsql文本中所处的位置并不会影响该sql的实际执行结果;


3.2、外连接(outer join)

外连接 是对内连接的一种扩展,它是指表连接的连接结果除了包含那些完全满足连接条件的记录之外还会包含驱动表中所有不满足该连接条件的记录。

外连接分为:左连接(left outer join ) 右连接(right outer join )和全连接(full outer join)

左连接的语法:(关键字左边的就是驱动表,即目标表1就是驱动表)

左连接的查询结果除了包含目标表1和目标表2中所有满足该连接条件的记录外,还包含驱动表中所有不满足该连接条件的记录,同时,驱动表中所有不满足该连接条件的记录所对应的被驱动表中的查询列均会以null值来填充。

目标表1 left outer join 目标表2 on (连接条件) 或 目标表1 left outer join 目标表2 using (连接列集合)

右连接的语法:(关键字右边的就是驱动表,即目标表2就是驱动表)

右连接的查询结果除了包含目标表1和目标表2中所有满足该连接条件的记录外,还包含驱动表中所有不满足该连接条件的记录,同时,驱动表中所有不满足该连接条件的记录所对应的被驱动表中的查询列均会以null值来填充。

目标表1 right outer join 目标表2 on (连接条件) 或 目标表1 right outer join 目标表2 using(连接列集合)

全连接的语法:

全连接查询结果除了包含目标表1和目标表2中所有满足该连接条件的记录外,还包含目标表1和目标表2中所有不满足该连接条件的记录,同时,驱动表中所有不满足该连接条件的记录所对应的被驱动表中的查询列均会以null值来填充。

目标表1 full outer join 目标表2 on (连接条件) 或 目标表1 full outer join 目标表2 using (连接列集合)

注意:

对于外连接而言,除了表连接条件之外的额外限制条件在目标sqlsql文本中所处的位置确实可能会影响该sql的实际执行结果。

Oracle自定义的关键字“(+)”来表示外连接:

关键字“(+)”出现在哪个表的连接列后面,就表名哪个表会以null值来填充那些不满足连接条件并位于该表中的查询列,

此时应该以关键字“(+)”对面的表来作为外连接的驱动表,这里的关键是决定哪个表是驱动表;


4、表连接的方法

在Oracle数据库中有四种表连接方法:排序合并连接、嵌套循环连接、哈希连接和笛卡尔连接


4.1、排序合并连接(sort merge join)

排序合并连接是一种两个表在做表连接时用排序操作(sort)和合并操作(merge)来得到连接结果集的表连接方法

排序合并连接的优缺点及适用场景:

①:通常情况下,排序合并连接的执行效率会远不如哈希连接,但前者的使用范围更广,因为哈希连接通常只能用于等值连接条件,

而排序合并连接还能用于其他连接条件(例如:< <= > >=)

②:通常情况下,排序合并连接并不适合OLTP类型的系统,因为对于OLTP类型的系统而言,排序是非常昂贵的操作,当然,如果能

避免排序操作,那么即使是OLTP类型的系统,也还是可以使用排序合并连接的。

③:从严格意义上来说,排序合并连接并不存在驱动表的概念;

4.2嵌套循环连接(nested loops join)

嵌套循环连接是一种两个表在做表连接时依靠两层嵌套循环(分别为外层循环和内层循环)来得到连接结果集的表连接方法

嵌套循环连接的优缺点及适用场景:

①:如果驱动表所对应的驱动结果集的记录较少,同时在被驱动表的连接列上又存在唯一性索引(或者在被驱动表的连接列上存在选择性很好的

非唯一性索引),那么此时使用嵌套循环连接的执行效率就会非常高。但如果驱动表所对应的驱动结果集的记录数很多,即便在被驱动表的连接

列上存在索引,此时使用嵌套循环连接的执行效率也不会高。

②:大表可以作为嵌套循环连接的驱动表,关键看目标sql中指定的谓词条件(如果有的话)能否将驱动结果集的数据量降下来;

③:嵌套循环连接有其他连接方法所没有的一个优点:嵌套循环连接可以实现快速响应。

4.3、哈希连接(hash join)

哈希连接是一种两个表在做连接时主要依靠哈希运算来得到连接结果集的表连接方法

注意:哈希连接只适用于CBO

从理论上来说,哈希连接的执行效率会比排序合并连接和嵌套循环连接要高,当然,实际情况并不总是这样。

哈希连接的优缺点及适用场景:

①:哈希连接不一定会排序,或者说大多数情况下都不需要排序

②:哈希连接的驱动表所对应的连接列的可选择性应尽可能好,因为这个可选择性会影响对应hash bucket中的记录数,

而hash bucket中的记录数又会直接影响从该 hash bucket中查找匹配记录的效率。

③:哈希连接只适用于CBO,它也只能用于等值连接条件(即使是哈希反连接,Oracle实际上也是将其转换成了等价的等值连接)

④:哈希连接很适合于小表和大表之间做表连接且连接结果集的记录数较多的情形,特别是在小表的连接列的可选择性非常好的情况下,

这时候哈希连接的执行时间就可以近似看作是全表扫描那个大表所耗费的时间相当;

⑤:当两个表做哈希连接时,如果在施加了目标sql中指定的谓词条件(如果有的话)后得到的数据量较小的那个结果集所对应的hash table

能够完全被容纳在内存中(PGA的工作区),则此时的哈希连接的执行效率会非常高;

4.4、笛卡尔连接(cross join)

笛卡尔连接又称为笛卡尔乘积,它是一种两个表在做表连接时没有任何连接条件的表连接方法

注意:笛卡尔连接实际上就是一种特殊的合并连接,这里的合并连接和排序合并连接类似,只不过笛卡尔连接不需要排序,并且在

执行合并操作时没有连接条件而已。


反连接:(anti join)

反连接是一种特殊的连接类型,与内连接和外连接不同,Oracle数据库里并没有相关的关键字可以在sql文本中专门表示反连接;

注意:当做子查询展开时,Oracle经常会把那些外部where条件为 not exists not in 或<>all 的子查询转换成对应的反连接;


半连接:(semi join)

半连接是一种特殊的连接类型,与反连接一样,Oracle数据库里也没有相关的关键字可以在sql文本中专门表示半连接;

注意:当做子查询时,Oracle经常会把那些外部where条件为exists in 或 =any的子查询转换为对应的半连接;


4.5、星型连接(star join)

星型连接通常用于数据仓库类型的应用,它是一种单个事实表(fact table)和多个维度表之间的连接;


------------------------------参考《基于Oracle的sql优化》---------------------------------

相关文章

Java Oracle 结果集是Java语言中处理数据库查询结果的一种方...
Java AES和Oracle AES是现代加密技术中最常使用的两种AES加密...
Java是一种广泛应用的编程语言,具备可靠性、安全性、跨平台...
随着移动互联网的发展,抽奖活动成为了营销活动中不可或缺的...
Java和Oracle都是在计算机领域应用非常广泛的技术,他们经常...
Java 是一门非常流行的编程语言,它可以运行于各种操作系统上...