–连接查询的表顺序
当对多个表进行连接查询时,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的使用方法:
注:sign()函数根据某个值是0、正数还是负数,分别返回0、1、-1
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;
–使用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将会进行隐式转换,这将影响执行时的性能,因此在编写程序代码时必须注意。