`

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优化器为了避免那些不必要的全表扫描(full table scan) , 你必须尽量避免使用CHOOSE优化器,而直接采用基于规则或者基于成本的优化器.

2访问Table的方式
ORACLE 采用两种访问表中记录的方式:
a.全表扫描
       全表扫描就是顺序地访问表中每条记录. ORACLE采用一次读入多个数据块(database block)的方式优化全表扫描.
   
b.通过ROWID访问表
       你可以采用基于ROWID的访问方式情况,提高访问表的效率, , ROWID包含了表中记录的物理位置信息..ORACLE采用索引(INDEX)实现了数据和存放数据的物理位置(ROWID)之间的联系通常索引提供了快速访问ROWID的方法,因此那些基于索引列的查询就可以得到性能上的提高

3共享SQL语句
为了不重复解析相同的SQL语句,在第一次解析之后, ORACLESQL语句存放在内存中.这块位于系统全局区域SGA(system global area)的共享池(shared buffer pool)中的内存可以被所有的数据库用户共享因此,当你执行一个SQL语句(有时被称为一个游标),如果它和之前的执行过的语句完全相同, ORACLE就能很快获得已经被解析的语句以及最好的
执行路径. ORACLE的这个功能大大地提高了SQL的执行性能并节省了内存的使用.
     可惜的是ORACLE只对简单的表提供高速缓冲(cache buffering) ,这个功能并不适用于多表连接查询.数据库管理员必须在init.ora中为这个区域设置合适的参数,当这个内存区域越大,就可以保留更多的语句,当然被共享的可能性也就越大了.
当你向ORACLE 提交一个SQL语句,ORACLE会首先在这块内存中查找相同的语句.
 这里需要注明的是,ORACLE对两者采取的是一种严格匹配,要达成共享,SQL语句必须
完全相同(包括空格,换行等).
     共享的语句必须满足三个条件:
A.字符级的比较:
当前被执行的语句和共享池中的语句必须完全相同(sql语句的大小写、中间的空格都要完全相同。)
B. 两个语句所指的对象必须完全相同
C. 两个SQL语句中必须使用相同的名字的绑定变量(bind variables)
比如,下面两个SQL语句就不一样:
select pin , name from people where pin = :blk1.ot_ind;
select pin , name from people where pin = :blk1.ov_ind;

4EXISTS替代IN
在许多基于基础表的查询中,为了满足一个条件,往往需要对另一个表进行联接.在这种情况下使用EXISTS(NOT EXISTS)通常将提高查询的效率
低效:
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)
5NOT EXISTS替代NOT IN
在子查询中,NOT IN子句将执行一个内部的排序和合并无论在哪种情况下,NOT IN都是最低效的 (因为它对子查询中的表执行了一个全表遍历).  为了避免使用NOT IN ,我们可以把它改写成外连接(Outer Joins)NOT EXISTS.

例如:
SELECT 
FROM EMP
WHERE DEPT_NO NOT IN (SELECT DEPT_NO
                         FROM DEPT
                         WHERE DEPT_CAT=A);

为了提高效率.改写为:

(方法一高效)
SELECT .
FROM EMP A,DEPT B
WHERE A.DEPT_NO = B.DEPT(+)
AND B.DEPT_NO IS NULL
AND B.DEPT_CAT(+) = A


(方法二最高效)
SELECT .
FROM EMP E
WHERE NOT EXISTS (SELECT X
                    FROM DEPT D
                    WHERE D.DEPT_NO = E.DEPT_NO
                    AND DEPT_CAT = A);

6用表连接替换EXISTS
通常来说 采用表连接的方式比EXISTS更有效率
      SELECT ENAME
      FROM EMP E
      WHERE EXISTS (SELECT X
                      FROM DEPT
                      WHERE DEPT_NO = E.DEPT_NO
                      AND DEPT_CAT = A);

     (更高效)
      SELECT ENAME
      FROM DEPT D,EMP E
      WHERE E.DEPT_NO = D.DEPT_NO
      AND DEPT_CAT = A’ ;
7EXISTS替换DISTINCT
当提交一个包含一对多表信息(比如部门表和雇员表)的查询时,避免在SELECT子句中使用DISTINCT. 一般可以考虑用EXIST替换

例如:
低效:
    SELECT DISTINCT DEPT_NO,DEPT_NAME
    FROM DEPT D,EMP E
    WHERE D.DEPT_NO = E.DEPT_NO
高效:
    SELECT DEPT_NO,DEPT_NAME
    FROM DEPT D
    WHERE EXISTS ( SELECT X
                    FROM EMP E
                    WHERE E.DEPT_NO = D.DEPT_NO);

  EXISTS 使查询更为迅速,因为RDBMS核心模块将在子查询的条件一旦满足后,立刻返回结果.

8选择最有效率的表名顺序(只在基于规则的优化器中有效)
ORACLE的解析器按照从右到左的顺序处理FROM子句中的表名,因此FROM子句中写在最后的表(基础表 driving table)将被最先处理FROM子句中包含多个表的情况下,你必须选择记录条数最少的表作为基础表.ORACLE处理多个表时会运用排序及合并的方式连接它们.首先,扫描第一个表(FROM子句中最后的那个表)并对记录进行派序,然后扫描第二个表(FROM子句中最后第二个表),最后将所有从第二个表中检索出的记录与第一个表中合适记录进行合并.
如果有3个以上的表连接查询那就需要选择交叉表(intersection table)作为基础表交叉表是指那个被其他表所引用的表.
9. WHERE子句中的连接顺序.
ORACLE采用自下而上的顺序解析WHERE子句,根据这个原理,表之间的连接必须写在其他WHERE条件之前那些可以过滤掉最大数量记录的条件必须写在WHERE子句的末尾.
10. SELECT子句中避免使用"*"
当你想在SELECT子句中列出所有的COLUMN,使用动态SQL列引用"*"是一个方便的方法.不幸的是,这是一个非常低效的方法实际上,ORACLE在解析的过程中会将*’ 依次转换成所有的列名这个工作是通过查询数据字典完成的这意味着将耗费更多的时间

11删除重复记录
最高效的删除重复记录方法 因为使用了ROWID)

DELETE FROM EMP E
WHERE E.ROWID > (SELECT MIN(X.ROWID)
                   FROM EMP X
                   WHERE X.EMP_NO = E.EMP_NO);
12TRUNCATE替代DELETE
13尽量多使用COMMIT
14计算记录条数
     和一般的观点相反, count(*) count(1)稍快 当然如果可以通过索引检索,对索引列的计数仍旧是最快的例如 COUNT(EMPNO)
15Where子句替换HAVING子句
     避免使用HAVING子句, HAVING 只会在检索出所有记录之后才对结果集进行过滤这个处理需要排序,总计等操作如果能通过WHERE子句限制记录的数目,那就能减少这方面的开销.

16 用IN来替换OR
下面的查询可以被更有效率的语句替换:
低效:
SELECT.
FROM LOCATION
WHERE LOC_ID = 10
OR     LOC_ID = 20
OR     LOC_ID = 30

高效
SELECT
FROM LOCATION
WHERE LOC_IN IN (10,20,30);
17避免在索引列上使用IS NULLIS NOT NULL
18总是使用索引的第一个列
如果索引是建立在多个列上只有在它的第一个列(leading column)where子句引用时,优化器才会选择使用该索引.
SQL> create table multiindexusage ( inda number , indb number , descr varchar2(10));
Table created.
SQL> create index multindex on multiindexusage(inda,indb);
Index created.
SQL> set autotrace traceonly

SQL>  select * from  multiindexusage where inda = 1;
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   TABLE ACCESS (BY INDEX ROWID) OF 'MULTIINDEXUSAGE'
   2    1     INDEX (RANGE SCAN) OF 'MULTINDEX' (NON-UNIQUE)

SQL> select * from  multiindexusage where indb = 1;
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   TABLE ACCESS (FULL) OF 'MULTIINDEXUSAGE'
  
很明显当仅引用索引的第二个列时,优化器使用了全表扫描而忽略了索引

19WHERE替代ORDER BY
ORDER BY 子句只在两种严格的条件下使用索引.

ORDER BY中所有的列必须包含在相同的索引中并保持在索引中的排列顺序.
ORDER BY中所有的列必须定义为非空.

WHERE子句使用的索引和ORDER BY子句中所使用的索引不能并列.
20使用日期
当使用日期是,需要注意如果有超过5位小数加到日期上这个日期会进到下一天!
例如:
a.
SELECT TO_DATE(01-JAN-93+.99999)
FROM DUAL;
Returns:
   ’01-JAN-93 23:59:59
b.
SELECT TO_DATE(01-JAN-93+.999999)
FROM DUAL;
Returns:
   ’02-JAN-93 00:00:00

21使用显式的游标(CURSORs)
使用隐式的游标,将会执行两次操作第一次检索记录第二次检查TOO MANY ROWS 这个exception . 而显式游标不执行第二次操作.

<!--EndFragment-->
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics