oracle的SQL文优化

Oracle数据库SQL文优化
1. 选用适合的ORACLE优化器
     ORACLE的优化器共有3种:a、RULE (基于规则) b、COST (基于成本) c、CHOOSE (选择性)
     设置缺省的优化器,可以通过对init.ora文件中OPTIMIZER_MODE参数的各种声明,如RULE,COST,CHOOSE,ALL_ROWS,FIRST_ROWS。也可以在SQL句级或是会话(session)
级对其进行覆盖。
    为了使用基于成本的优化器(CBO, Cost-Based Optimizer),必须经常运行analyze 命令,以增加数据库中的对象统计信息(object statistics)的准确性。
    如果数据库的优化器模式设置为选择性(CHOOSE),那么实际的优化器模式将和是否运行过analyze命令有关。如果table已经被analyze过,优化器模式将自动成为CBO,反之,数据
库将采用RULE形式的优化器。
    在缺省情况下,ORACLE采用CHOOSE优化器,为了避免那些不必要的全表扫描,必须尽量避免使用CHOOSE优化器,而直接采用基于规则或者基于成本的优化器。
2、通过ROWID访问表
    可以采用基于ROWID的访问方式,提高访问表的效率,ROWID包含了表中记录的物理位置信息。ORACLE采用索引(INDEX)实现了数据和存放数据的物理位置(ROWID)之间的联系。
通常索引提供了快速访问ROWID的方法,因此那些基于索引列的查询就可以得到性能上的提高。
3、共享SQL语句
    为了不重复解析相同的SQL语句,在第一次解析之后, ORACLE将SQL语句存放在内存中。这块位于系统全局区域SGA(system global area)的共享池(shared buffer pool)中的内存可
以被所有的数据库用户共享.。因此,当你执行一个SQL语句(有时被称为一个游标)时,如果它和之前的执行过的语句完全相同, ORACLE就能很快获得已经被解析的语句以及最好的 执行路径。ORACLE的这个功能大大地提高了SQL的执行性能并节省了内存的使用。可惜的是ORACLE只对简单的表提供高速缓冲(cache buffering),这个功能并不适用于多表连接查询.。在init.ora中为这个区域设置合适的参数,当这个内存区域越大,就可以保留更多的语句,当然被共享的可能性也就越大了。
    当向ORACLE提交一个SQL语句,ORACLE会首先在这块内存中查找相同的语句。这里需要注明的是,ORACLE对两者采取的是一种严格匹配,要达成共享,SQL语句必须完全相同(
包括大小写,空格,换行等)。
    共享的语句必须满足三个条件:
    A、字符级的比较:
    当前被执行的语句和共享池中的语句必须完全相同。
    B、两个语句所指的对象必须完全相同(用户名、对象、访问方式)
    C、两个SQL语句中必须使用相同的名字的绑定变量(bind variables)
4、选择最有效率的表名顺序(只在基于规则的优化器中有效)
     ORACLE的解析器按照从右到左的顺序处理FROM子句中的表名,因此FROM子句中写在最后的表(基础表)将被最先处理。在FROM子句中包含多个表的情况下,必须选择记录条数最
少的表作为基础表。当ORACLE处理多个表时,会运用排序及合并的方式连接它们。首先,扫描第一个表(FROM子句中最后的那个表)并对记录进行排序,然后扫描第二个表(FROM子句中最后第二个表),最后将所有从第二个表中检索出的记录与第一个表中合适记录进行合并。如果有3个以上的表连接查询,那就需要选择交叉表作为基础表,交叉表是指那个被其他表所引用的表。
5、WHERE子句中的连接顺序
   ORACLE采用自下而上的顺序解析WHERE子句,根据这个原理,表之间的连接必须写在其他WHERE条件之前,那些可以过滤掉最大数量记录的条件必须写在WHERE子句的末尾。
6、SELECT子句中避免使用‘*’
    ORACLE在解析的过程中,会将'*'依次转换成所有的列名,这个工作是通过查询数据字典完成的,这意味着将耗费更多的时间。
7、减少访问数据库的次数
方法1(最低效)
SELECT EMP_NAME , SALARY , GRADE
FROM EMP
WHERE EMP_NO = 342;

SELECT EMP_NAME , SALARY , GRADE
FROM EMP
WHERE EMP_NO = 291;
方法2(高效)
SELECT A.EMP_NAME , A.SALARY , A.GRADE,
B.EMP_NAME , B.SALARY , B.GRADE
FROM EMP A,EMP B
WHERE A.EMP_NO = 342
AND B.EMP_NO = 291;
8、使用DECODE函数来减少处理时间
    使用DECODE函数可以避免重复扫描相同记录或重复连接相同的表。
例如:
SELECT COUNT(*),SUM(SAL)
FROM EMP
WHERE DEPT_NO = 0020
    AND ENAME LIKE ‘SMITH%'

SELECT COUNT(*),SUM(SAL)
FROM EMP
WHERE DEPT_NO = 0030
    AND ENAME LIKE ‘SMITH%'

使用DECODE函数:
SELECT COUNT(DECODE(DEPT_NO,0020,'X',NULL)) D0020_COUNT,
    COUNT(DECODE(DEPT_NO,0030,'X',NULL)) D0030_COUNT,
    SUM(DECODE(DEPT_NO,0020,SAL,NULL)) D0020_SAL,
    SUM(DECODE(DEPT_NO,0030,SAL,NULL)) D0030_SAL
FROM  EMP
WHERE ENAME LIKE ‘SMITH%'
9、删除重复记录
    最高效的删除重复记录方法(因为使用了ROWID)
DELETE FROM EMP E
WHERE E.ROWID > (SELECT MIN(X.ROWID)
          FROM EMP X
          WHERE X.EMP_NO = E.EMP_NO)
10、用TRUNCATE替代DELETE
     当删除表中的记录时,在通常情况下,回滚段用来存放可以被恢复的信息。如果没有COMMIT事务,ORACLE会将数据恢复到删除之前的状态。而当运用TRUNCATE时,回滚段不再存
放任何可被恢复的信息,当命令运行后,数据不能被恢复,因此很少的资源被调用,执行时间也会很短。TRUNCATE只在删除全表适用,TRUNCATE是DDL不是DML。
11、尽量多使用COMMIT
    只要有可能,在程序中尽量多使用COMMIT,这样程序的性能得到提高,需求也会因为COMMIT所释放的资源而减少。
  COMMIT所释放的资源:
  a、回滚段上用于恢复数据的信息。
  b、被程序语句获得的锁。
  c、redo log buffer 中的空间。
  d、ORACLE为管理上述3种资源中的内部花费
12、用Where子句替换HAVING子句
13、减少对表的查询
14、通过内部函数提高SQL效率
15、使用表的别名
16、用EXISTS替代IN
低效:
SELECT  *
    FROM EMP (基础表)
WHERE EMPNO > 0
       AND DEPTNO IN (SELECT DEPTNO
                FROM DEPT
               WHERE LOC = ‘MELB')
高效:
SELECT *
  FROM EMP (基础表)
WHERE EMPNO > 0
    AND EXISTS (SELECT ‘X'
                FROM DEPT
            WHERE DEPT.DEPTNO = EMP.DEPTNO
                AND LOC = ‘MELB')
17、用NOT EXISTS替代NOT IN
    在子查询中,NOT IN子句将执行一个内部的排序和合并。无论在哪种情况下,NOT IN都是最低效的(因为它对子查询中的表执行了一个全表遍历)。为了避免使用NOT IN,我们可以
把它改写成外连接或NOT EXISTS。
18、用表连接替换EXISTS
19、用EXISTS替换DISTINCT
20、用IN来替换OR
21、用UNION-ALL 替换UNION
    当SQL语句需要UNION两个查询结果集合时,这两个结果集合会以UNION-ALL的方式被合并,然后在输出最终结果前进行排序。如果用UNION ALL替代UNION,这样排序就不是必
要了,效率就会因此得到提高。
22、使用提示(Hints)
23、使用索引

识别低效执行的SQL语句:
  SELECT EXECUTIONS,
       DISK_READS,
       BUFFER_GETS,
       ROUND((BUFFER_GETS-DISK_READS)/BUFFER_GETS,2) Hit_Radio,
       ROUND(DISK_READS/EXECUTIONS,2) Reads_Per_Run,
       SQL_TEXT
   FROM  V$SQLAREA
   WHERE EXECUTIONS > 0 AND BUFFER_GETS > 0 AND (BUFFER_GETS-DISK_READS)/BUFFER_GETS < 0.8
ORDER BY Hit_Radio DESC;
另外,通过SQL/PLUS也可以来跟踪SQL文的执行,set autotrace traceonly /*traceonly 可以不显示执行结果

你可能感兴趣的:(oracle,sql,cache)