ORACLE-SQL调优

–连接查询的表顺序
当对多个表进行连接查询时,Oracle分析器会按照从右到左的顺序处理FROM字句中的表名。
例如:

SELECT a.empno, a.ename, c.deptno, c.dname, a.log_action
  FROM emp_log a, emp b, dept c 

在执行时,Oracle会先查询dept表,根据dept表查询的行作为数据源串行连接emp表继续执行,因此dept表又称为基础表或驱动表。由于连接的顺序对于查询的效率有非常大的影响,因此在处理多表连接时,必须选择记录条数较少的表作为基础表。 Oracle会使用排序与合并的方式进行连接。比如先扫描dept表,然后对dept表进行排序,再扫描emp表,最后将所有检索出来的记录与第一个表中的记录进行合并。

–指定WHERE条件顺序
在查询表时,WHERE子句中条件的顺序往往影响了执行的性能。默认情况下,Oracle采用自下而上的顺序解析WHERE子句,因此在处理多表查询时,表之间的连接必须写在其他WHERE条件之前,但是过滤数据记录的条件则必须写在WHERE子句的尾部, 以便在过滤了数据之后再进行连接处理,这样可以提升SQL语句的性能。

–避免使用*符号
Oracle在遇到*符号时,会去查询数据字典表中获取所有的列信息,然后依次转换成所有的列名, 这将耗费较长的执行时间,因此在编写程序代码时,应尽量避免使用*号获取所有的列信息。

–使用DECODE函数
DECODE函数是Oracle数据库才具有的一个功能强大的函数,灵活使用该函数可以减少很多对表的不必要的访问。 因此多次对一个表进行读取操作会严重降低性能,为此可以使用DECODE来解决。
DECODE的使用方法:

  1. 使用decode判断字符串是否一致
    decode(value, if1, then1, if2, then2, if3, then3, … ., else)
  2. 使用decode比较大小
    decode(sign(var1 - var2), 1, var1, var2)

注:sign()函数根据某个值是0、正数还是负数,分别返回0、1、-1

  1. 使用decode分段
    工资大于10000为高薪,5000-10000为中等,小于5000为低等
    示例:
select name,
       sal,
       DECODE(SIGN(sal - 10000),
              1,
              'high sal',
              0,
              'high sal',
              DECODE(SIGN(sal - 5000), 1, 'mid sal', 0, 'mid sal', 'low sql'))
  from emp;

4. 使用decode实现表或者视图的行列转换

select count(decode(deptno, '20', 'X', null)) dept20_count,
       count(decode(deptno, '30', 'X', null)) dept30_count,
       sum(decode(deptno, '20', sal, null)) dept20_sal,
       sum(decode(deptno, '30', sal, null)) dept30_sal
  from emp;
  1. 使用decode函数来使用表达式搜索字符串
    decode (expression, search_1, result_1, search_2, result_2, …, search_n, result_n, default)
    decode函数比较表达式和搜索字,如果匹配,返回结果;如果不匹配,返回default值;如果未定义default值,则返回空值。

–使用WHERE而非HAVING
WHERE语句是在GROUP BY 语句之前筛选出记录,而HAVING是在各种记录都筛选之后再进行过滤。 也就是说HAVING子句是在从数据库中提取数据之后进行筛选的,因此在编写SQL语句时,尽量在筛选之前将数据使用WHERE子句进行过滤。执行顺序:
 使用WHERE子句查找符合条件的数据。
 使用GROUP BY子句对数据进行分组。
 在GROUP BY分组的基础上运行聚合函数计算每一组的值。
 用HAVING子句去掉不符合条件的组。

–使用UNION而非OR
如果考虑在SQL查询中使用OR语句,那么需要特别注意其性能问题。如果要进行OR运算的两个列都是索引列,可以考虑使用UNION来提升性能。
如果坚持使用OR语句,需要记住尽量将返回记录最少的索引列写在最前面,这样能获得较好的性能。
在要对单个字段值进行OR计算的时候,可以考虑使用IN来代替。

–使用EXISTS而非IN
在编写SQL语句时,使用IN语句可以解决很多问题,比如要查询位于芝加哥的所有员工列表,可以考虑使用如下的IN查询:

select *
  from emp
 where deptno in (select deptno from dept where loc = 'CHICAGO')

在查询语句中,IN后面使用了子查询来获取位于芝加哥的部门编号的列表,==可以将IN语句更改为使用EXISTS来获得更好的查询性能,==如以下代码所示:

select *
  from emp
 where exists (select deptno from dept where loc = 'CHICAGO')

同样的替换也发生在NOT IN和NOT EXISTS之间。NOT IN子句将执行一个内部的排序和合并,实际上它对子查询中的表执行了一次全表扫描,因此效率较低,在需要使用NOT IN的场合,应该总是考虑把他更改为外连接或NOT EXISTS。
例如要查询emp表中员工所在部门不为芝加哥的员工列表,如果使用NOT IN,语法如下所示:

select *
  from emp
 where deptno not in (select deptno from dept where loc = 'CHICAGO')

为了提供较好的性能,可以考虑使用连接查询,如下代码所示:

select a.*
  from emp a, dept b
 where a.deptno = b.deptno and b.loc <> 'CHICAG

这是最有效率的一种办法,但是也可以考虑NOT EXISTS,如以下代码所示:

select a.*
  from emp a
 where not exists (select 1 from dept b where a.deptno = b.deptno and b.loc = 'CHICAGO')

–避免低效的PL/SQL流程控制语句
PL/SQL在处理逻辑表达式值的时候,使用的是短路径的计算方式。
因此==应该总是将开销较低的判断语句放在前面,==这样当前面的判断失败时,就不会再执行后面的具有较高开销的语句,能提升PL/SQL应用程序的性能。

–避免隐式类型的转换
当将一种类型的变量赋给另一种兼容的数据类型时,比如把PLS_INTEGER变量赋给一个NUMBER类型的变量时,==由于其内在的表现形式不同,因此会引起Oracle进行隐式数据类型的转换。==举例来说,在进行PL/SQL程序设计时,建议对整型的操作使用PLS_INTEGER和BINARY_INTEGER这两种类型,这两种类型提供了相对于NUMBER类型更好的性能。但是当需要把数据库存储到Oracle中时,需要将其转换为NUMBER类型,这将导致类型的转换,引起性能问题。
但是对于数字类型来说,如果操作不涉及数据库操作,应该总是使用PL/SQL提供的类型,比如下面的示例代码使用了NUMBER类型,并不涉及数据库操作:

DECLARE
  wk_cnt NUMBER := 0; -- 定义NUMBER类型
BEGIN
  FOR cnt IN 0 .. 100000000 LOOP
    wk_cnt := wk_cnt + 1; -- 累加
  END LOOP;
END

想较于使用PLS_INTEGER类型,使用NUMBER类型虽然便于移植并且能适应不同的长度与精度,但是PLS_INTEGER所需要的内存比INTEGET和NUMBER类型要少,并且使用机器进行运算,其运算速度要快得多。
但是如果要将一个字符串类型的数字赋给一个数据类型的变量,Oracle将会进行隐式转换,这将影响执行时的性能,因此在编写程序代码时必须注意。

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