pl/sql性能优化技巧

1.为什么要进行SQL语句的性能优化

      对于数据量不大的数据操作中,SQL语句的优不优化操作所带来的收益并不大,但是当数据量增加到大量的情况下时,SQL的执行时间就会清楚的告诉我们语句效率的高低之分,每一条SQL语句的执行都有一定的步骤和顺序,遵循优化的规则编写SQL语句,可以从根本上优化数据的增删改查操作(这里尤其突出“查”的操作,因为我们知道对数据库操作有80%都是查的操作,而增删改占比20%),从而提高SQL的执行效率。通往罗马的路有很多,寻找到最优的一条,我们才能更快更好的到达罗马。这就是我们之所以要对SQL语句优化的原因。

2.如何测试我们编写的SQL 语句的性能

    oracle专门提供了对SQL语句性能分析的功能,就是执行计划,通过对执行计划的分析,我们可以清楚的知道SQL语句执行的效率。

什么是执行计划?

     oracle数据库在执行SQL语句时,oracle的优化器会根据一定的规则确定SQL语句的执行路径,以确保SQL语句能以最优性能执行.在oracle数据库系统中为了执行SQL语句,oracle可能需要实现多个步骤,这些步骤中的每一步可能是从数据库中物理检索数据行,或者用某种方法准备数据行,让编写SQL语句的用户使用,oracle用来执行语句的这些步骤的组合被称为执行计划。

当执行一个SQL语句时oracle经过了4个步骤:

     ①.解析SQL语句:主要在共享池中查询相同的SQL语句,检查安全性和SQL语法与语义。

     ②.创建执行计划及执行:包括创建SQL语句的执行计划及对表数据的实际获取。

     ③.显示结果集:对字段数据执行所有必要的排序,转换和重新格式化。

     ④.转换字段数据:对已通过内置函数进行转换的字段进行重新格式化处理和转换.

明白SQL语句的执行流程和执行计划的概念,我们现在应该要知道如何使用执行计划,查看SQL语句的性能:

1.pl/sql中选中SQL语句,按F5即可以调出执行计划窗口,如下所示

《pl/sql性能优化技巧》

上面就是便是对于sql语句的执行计划,具体参数分析如下:

基数(Rows):Oracle估计的当前操作的返回结果集行数;

字节(Bytes):执行该步骤后返回的字节数;

耗费(COST)、CPU耗费:Oracle估计的该步骤的执行成本,用于说明SQL执行的代价,理论上越小越好(该值可能与实际有出入);

时间(Time):Oracle估计的当前操作所需的时间;

Description中参数的解析:

    (1) 全表扫描(TABLE ACCESS FULL )

        Oracle会读取表中所有的行,并检查每一行是否满足SQL语句中的 Where 限制条件;

        全表扫描时可以使用多块读(即一次I/O读取多块数据块)操作,提升吞吐量;

        使用建议:数据量太大的表不建议使用全表扫描,除非本身需要取出的数据较多,占到表数据总量的 5% ~ 10% 或以上

    (2) 通过ROWID的表存取(TABLE ACCESS BY ROWID)

        行的rowid指出了该行所在的数据文件,数据块及行在该块中的位置,所以通过rowid来存取数据可以快速定位到目标数据上,是oracle存取单行数据的最快方法。

    (3) 索引扫描(TABLE ACCESS BY INDEX SCAN)

         先通过索引找到对象的rowid值,然后通过rowid值直接从表中找到具体的数据,能大大提高查找的效率。

SQL性能判断依据:

1.SQL执行的时间尽量短;

2.耗费和CPU耗费尽可能的降低;

3.观察Description的参数,有索引的字段应当显示索引扫描,出现全表扫描时一定要进行推敲是否合理。

性能的比较是在于,对于获得同意结果的sql语句,进行执行计划的分析,获取最优的SQL语句,那么所获得的结果(也就是字节数和基数),应当是不改变的。

—————————————————————————-

以上都是对于SQL语句性能的分析和查看,下面我们就要进行对SQL语句的优化

优化规则一:注意连接查询时的表顺序

        首先先明白Oracle分析器对表数据进行连接查询的规则是:从右到左的顺序处理from子句的表,以第一张表为基础表(驱动表)进行排序和合并的方式进行连接。

        因此连接查询时表顺序的规则为:

当连接表为两张时,必须选择记录条数的表作为基础表;(基础表就是按照连接查询规则,从右至左的第一张表则为基础表)

当连接表超过两张时,需要优先选择交叉表作为基础表,若没有交叉表时,则优先选择记录条数较少的表作为基础表。(交叉表为既被A表也被B表所引用的表,既包含A表内容也包含B表内容的C表)

原因在于:选择少记录条数的表作为基础表的原因在于,减少表与表之间合并的基准数,以谁作为基础表,那么其基准数据量就以谁,如果基准表的数据量很大,那么会造成很多不必要的冗余数据产生。

优化规则二:指定where的条件顺序

       首先先要明白Oracle分析器对where子句的执行顺序的规则是:自下而上的顺序解析where子句(也就是从后往前的执行)

        因此where的条件顺序规则为:

    表连接条件必须在where条件中的前部;

    过滤条件必须在where条件中的尾部。

原因在于:在处理多表连接查询时,应给先要进行数据的筛选再进行数据是排序和合并,这样可以先筛选掉部分无用的数据,合并多表数据时,整体速度就得到了一定的提高。

优化规则三:避免使用*符号

原因在于:使用*符号,Oracle需要先寻找到表对应的数据字典信息,将数据字典中的所有列信息取出来,然后依次转化为列名,再进行数据的显示,在这个转化的过程就已经耗费了一定的时间,降低了效率。

优化规则四:多使用where而不是having作为筛选语句

        首先先要明白where子句和having子句作用的具体情况:

        1.where子句和having子句都可以过滤数据,但是where子句不能使用聚集函数,如count max min avg sum等函数。

        2.通常将Having子句与Group By子句一起使用,当利用Group By进行分组时,可以没有Having子句,但Having出现时,一定会有Group By。

       3.WHERE语句是在GROUP BY语句之前筛选出记录,而HAVING是在各种记录都筛选之后再进行过滤。也就是说HAVING子句是在从数据库中提取数据之后进行筛选的,因此在编写SQL语句时,尽量在筛选之前将数据使用WHERE子句进行过滤。

因此执行的顺序应该总是这样:

  ①.使用WHERE子句查询符合条件的数据;

  ②.使用GROUP BY子句对数据进行分组;

  ③.在GROUP BY分组的基础上运行聚合函数计算每一组的值;

  ④.用HAVING子句去掉不符合条件的组。

这里再补充几大子句的使用顺序:

where 子句 — group by子句 — having 子句 — order by子句

原因在于:先多数据进行筛选再进行有效数据分组,这样可以大大的减少冗余数据量,提高效率。

优化规则五:合理的选择in和exists;使用not exists代替not in

        首先也要先明白:

        in和exists的原理,区别

            1.in 是把外表和内表作Hash Join连接,但是此时是将内表(存在in判断的表)作为基础表(驱动表)进行连接的,在

此就可以引出如何选择这两者的条件,in不对null进行判断;

            2.exists 是把外表作为循环,每一次的循环都会对内表的条件进行判断,一步步的遍历判断找到符合条件的数据。

            合理选择in和exists的条件:

                当子查询所查询出的结果集少,而主查询数据量大时,选择in反而比exists更高效;当子查询所查询出的结果集大,而主查询数据量小时,选择exists比in更高效。

        not in和not exists的原理,区别

            1.not in 是最为耗时耗资源的方式,将内外表均进行全表扫描,不使用索引,可想而知not in方式是非常不明智的选择;

            2.not exists 则不然,主查询仍然是遍历主表的所有数据,但是子查询中可以使用索引进行查询,从而大大提高效率。

总结:in与exists应该合理的选择;而not in和not exists则应该使用not exists代替not in。

优化规则六:or两边的字段都是有索引时,尽量使用union进行替代

UNION 操作符用于合并两个或多个 SELECT 语句的结果集。

请注意,1.UNION 内部的 SELECT 语句必须拥有相同数量的列;2.列也必须拥有相似的数据类型;3.每条 SELECT 语句中的列的顺序必须相同。

默认地,UNION 操作符选取不同的值(去除重复值)。如果允许重复的值,请使用 UNION ALL。

清楚其实union连接的两条语句,如果判断字段是有索引的,那么会分别进行索引扫描,若两者都没有索引,那就相当于两次全表扫描,效率就还不如or了。

使用union替代or的前提条件在于:作用的两个列字段都是有索引的,如果都是没有索引的话,那还不如用or的效率高;还有一点是如果字段相同,所要判断的值不同的话,使用in的效率比or的更高。

原因在于:使用or时会使引擎不使用索引扫描,而采用全表扫描,这样效率则极为低下,使用union分开之后,两个查询都使用了索引扫描,那么效率很不同了。

empno和ename均有索引的情况下

select empno,ename,job,sal from emp where empno > 7500 OR ename LIKE ‘S%’;  

使用union

select empno,ename,job,sal from emp where empno > 7500   

UNION  

select empno,ename,job,sal from emp where ename LIKE ‘S%’;  

优化规则七:多使用decode函数来替代IF ELSE和UNION语句

IF ELSE语句主要是繁琐的嵌套判断,这就好比我们常用三元运算符来取代if else的判断,这样既可以简化代码,也可以提高部分效率;

UNION语句主要是连接两条具有相同输出结果的SQL语句,本质上来说还是两条SQL语句的执行,只是将结果进行重复(union)或者不重复(union all)的合并;

而decode函数则是一条SQL语句进行多类型的操作,简化了需要嵌套判断的过程(包含三元运算符的意思),也取代了分多条SQL语句进行操作的问题,使用一条SQL就可以解决众多的问题,一次扫描即可,那么可想而知效率肯定是提高不少的。

使用union解决

select count(*),SUM(sal) from emp where deptno=20;  

union  

select count(*),SUM(sal) from emp where deptno=30; 

使用decode函数

SELECT COUNT (DECODE (deptno, 20, ‘X’, NULL)) dept20_count,  

        COUNT (DECODE (deptno, 30, ‘X’,NULL)) dept30_count,  

        SUM (DECODE (deptno, 20, sal, 0)) dept20_sal,  

        SUM (DECODE (deptno, 30, sal, 0)) dept30_sal  

  FROM emp;

具体的decode函数的用法,详见另一笔记《Decode函数的用法》

以下为项目中用到的优化

原生:selectcount(*)fromv_group_dynamic_msg dwhered.group_id=110000000000920andd.crente_date>=(selectm.last_visit_datefromv_grp_member mwherem.psn_id=1000000727052andm.grp_id=d.group_id)

改为exists

优化:selectcount(*)fromv_group_dynamic_msg dwhered.group_id=110000000000920andexists(select1fromv_grp_member mwherem.psn_id=1000000727052andm.grp_id=d.group_idandd.crente_date>=m.last_visit_date)

筛选再判断

再优化:selectcount(*)fromv_group_dynamic_msg dwhereexists(select1fromv_grp_member mwhered.crente_date>=m.last_visit_dateandm.psn_id=1000000727052andm.grp_id=d.group_id)andd.group_id=110000000000920

    原文作者:JackEason
    原文地址: https://www.jianshu.com/p/ca1c27198baa
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞