`

SQL优化的一些注意事项

阅读更多

4. 选择最有效率的表名顺序(只在基于规则的优化器中有效)
ORACLE的解析器按照从右到左的顺序处理FROM子句中的表名,因此FROM子句中写在最后的表(基础表 driving table)将被最先处理. 在FROM子句中包含多个表的情况下,你必须选择记录条数最少的表作为基础表.当ORACLE处理多个表时, 会运用排序及合并的方式连接它们.首先,扫描第一个表(FROM子句中最后的那个表)并对记录进行派序,然后扫描第二个表(FROM子句中最后第二个表),最后将所有从第二个表中检索出的记录与第一个表中合适记录进行合并.
例如:      表 TAB1 16,384 条记录
         表 TAB2 1      条记录
     选择TAB2作为基础表 (最好的方法)
     select count(*) from tab1,tab2   执行时间0.96秒
     选择TAB2作为基础表 (不佳的方法)


--------------------------------------------------------------------------------
 txfy 回复于:2003-12-02 10:06:03

select count(*) from tab2,tab1   执行时间26.09秒
如果有3个以上的表连接查询, 那就需要选择交叉表(intersection table)作为基础表, 交叉表是指那个被其他表所引用的表.
例如:   EMP表描述了LOCATION表和CATEGORY表的交集.
SELECT * 
FROM LOCATION L , 
      CATEGORY C,
      EMP E 
WHERE E.EMP_NO BETWEEN 1000 AND 2000
AND E.CAT_NO = C.CAT_NO
AND E.LOCN = L.LOCN
 将比下列SQL更有效率
SELECT * 
FROM EMP E ,
LOCATION L , 
      CATEGORY C
WHERE  E.CAT_NO = C.CAT_NO
AND E.LOCN = L.LOCN
AND E.EMP_NO BETWEEN 1000 AND 2000

 
5.       WHERE子句中的连接顺序.
   ORACLE采用自下而上的顺序解析WHERE子句,根据这个原理,表之间的连接必须写在其他WHERE条件之前, 那些可以过滤掉最大数量记录的条件必须写在WHERE子句的末尾.
 例如:
 (低效,执行时间156.3秒)
SELECT … 
FROM EMP E
WHERE  SAL >; 50000
AND    JOB = ‘MANAGER’
AND    25 < (SELECT COUNT(*) FROM EMP


--------------------------------------------------------------------------------
 txfy 回复于:2003-12-02 10:06:21

WHERE MGR=E.EMPNO);
 (高效,执行时间10.6秒)
SELECT … 
FROM EMP E
WHERE 25 < (SELECT COUNT(*) FROM EMP
             WHERE MGR=E.EMPNO)
AND    SAL >; 50000
AND    JOB = ‘MANAGER’;


6.     SELECT子句中避免使用 ‘ * ‘
当你想在SELECT子句中列出所有的COLUMN时,使用动态SQL列引用 ‘*’ 是一个方便的方法.不幸的是,这是一个非常低效的方法. 实际上,ORACLE在解析的过程中, 会将’*’ 依次转换成所有的列名, 这个工作是通过查询数据字典完成的, 这意味着将耗费更多的时间. 


7.     减少访问数据库的次数
当执行每条SQL语句时, ORACLE在内部执行了许多工作: 解析SQL语句, 估算索引的利用率, 绑定变量 , 读数据块等等. 由此可见, 减少访问数据库的次数 , 就能实际上减少ORACLE的工作量.
 例如,
    以下有三种方法可以检索出雇员号等于0342或0291的职员.
 方法1 (最低效)
    SELECT EMP_NAME , SALARY , GRADE
    FROM EMP 
    WHERE EMP_NO = 342;
     SELECT EMP_NAME , SALARY , GRADE
    FROM EMP 
    WHERE EMP_NO = 291;
方法2 (次低效)
       DECLARE 
        CURSOR C1 (E_NO NUMBER) IS 
        SELECT EMP_NAME,SALARY,GRADE


--------------------------------------------------------------------------------
 txfy 回复于:2003-12-02 10:06:58

FROM EMP 
        WHERE EMP_NO = E_NO;
    BEGIN 
        OPEN C1(342);
        FETCH C1 INTO …,..,.. ;
                OPEN C1(291);
       FETCH C1 INTO …,..,.. ;
         CLOSE C1;
      END;
方法3 (高效)
    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;
 注意:
在SQL*Plus , SQL*Forms和Pro*C中重新设置ARRAYSIZE参数, 可以增加每次数据库访问的检索数据量 ,建议值为200.


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%’;
类似的,DECODE函数也可以运用于GROUP BY 和ORDER BY子句中.


9.       整合简单,无关联的数据库访问
如果你有几个简单的数据库查询语句,你可以把它们整合到一个查询中(即使它们之间没有关系)
例如:
 SELECT NAME 
FROM EMP 
WHERE EMP_NO = 1234;

 SELECT NAME 
FROM DPT
WHERE DPT_NO = 10 ;

SELECT NAME 
FROM CAT
WHERE CAT_TYPE = ‘RD’;
上面的3个查询可以被合并成一个:
 SELECT E.NAME , D.NAME , C.NAME
FROM CAT C , DPT D , EMP E,DUAL X
WHERE NVL(‘X’,X.DUMMY) = NVL(‘X’,E.ROWID(+))
AND NVL(‘X’,X.DUMMY) = NVL(‘X’,D.ROWID(+))
AND NVL(‘X’,X.DUMMY) = NVL(‘X’,C.ROWID(+))
AND E.EMP_NO(+) = 1234
AND D.DEPT_NO(+) = 10
AND C.CAT_TYPE(+) = ‘RD’;
 (译者按: 虽然采取这种方法,效率得到提高,但是程序的可读性大大降低,所以读者 还是要权衡之间的利弊)


10.       删除重复记录
最高效的删除重复记录方法 ( 因为使用了ROWID)
DELETE FROM EMP E
WHERE E.ROWID >; (SELECT MIN(X.ROWID) 
                   FROM EMP X
                   WHERE X.EMP_NO = E.EMP_NO);


13.       计算记录条数
     和一般的观点相反, count(*) 比count(1)稍快 , 当然如果可以通过索引检索,对索引列的计数仍旧是最快的. 例如 COUNT(EMPNO)
  (译者按: 在CSDN论坛中,曾经对此有过相当热烈的讨论, 作者的观点并不十分准确,通过实际的测试,上述三种方法并没有显著的性能差别)



 14.       用Where子句替换HAVING子句
      避免使用HAVING子句, HAVING 只会在检索出所有记录之后才对结果集进行过滤. 这个处理需要排序,总计等操作. 如果能通过WHERE子句限制记录的数目,那就能减少这方面的开销.
 例如:
     低效:
     SELECT REGION,AVG(LOG_SIZE)
     FROM LOCATION
     GROUP BY REGION
     HAVING REGION REGION != ‘SYDNEY’
     AND REGION != ‘PERTH’
      高效
     SELECT REGION,AVG(LOG_SIZE)
     FROM LOCATION
     WHERE REGION REGION != ‘SYDNEY’
     AND REGION != ‘PERTH’
     GROUP BY REGION
(译者按: HAVING 中的条件一般用于对一些集合函数的比较,如COUNT() 等等. 除此而外,一般的条件应该写在WHERE子句中)


15.       减少对表的查询
在含有子查询的SQL语句中,要特别注意减少对表的查询.
  例如: 
     低效
          SELECT TAB_NAME
          FROM TABLES
          WHERE TAB_NAME = ( SELECT TAB_NAME 
                                FROM TAB_COLUMNS
                                WHERE VERSION = 604)
          AND DB_VER= ( SELECT DB_VER 
                           FROM TAB_COLUMNS
                           WHERE VERSION = 604)
     高效
          SELECT TAB_NAME
          FROM TABLES
          WHERE  (TAB_NAME,DB_VER)
 = ( SELECT TAB_NAME,DB_VER) 
                   FROM TAB_COLUMNS
                   WHERE VERSION = 604)
     Update 多个Column 例子:
     低效:
           UPDATE EMP
           SET EMP_CAT = (SELECT MAX(CATEGORY) FROM EMP_CATEGORIES),
              SAL_RANGE = (SELECT MAX(SAL_RANGE) FROM EMP_CATEGORIES)
           WHERE EMP_DEPT = 0020;
     高效:
           UPDATE EMP
           SET (EMP_CAT, SAL_RANGE)
 = (SELECT MAX(CATEGORY) , MAX(SAL_RANGE)
 FROM EMP_CATEGORIES)
           WHERE EMP_DEPT = 0020;


17.       使用表的别名(Alias)
当在SQL语句中连接多个表时, 请使用表的别名并把别名前缀于每个Column上.这样一来,就可以减少解析的时间并减少那些由Column歧义引起的语法错误.
  (译者注: Column歧义指的是由于SQL中不同的表具有相同的Column名,当SQL语句中出现这个Column时,SQL解析器无法判断这个Column的归属)


18.       用EXISTS替代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’)
(译者按: 相对来说,用NOT EXISTS替换NOT IN 将更显著地提高效率,下一节中将指出)



19.       用NOT 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’);


20.       用表连接替换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’ ;
  (译者按: 在RBO的情况下,前者的执行路径包括FILTER,后者使用NESTED LOOP)


 21.       用EXISTS替换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核心模块将在子查询的条件一旦满足后,立刻返回结果.




36.       用UNION替换OR (适用于索引列)
通常情况下, 用UNION替换WHERE子句中的OR将会起到较好的效果. 对索引列使用OR将造成全表扫描. 注意, 以上规则只针对多个索引列有效. 如果有column没有被索引, 查询效率可能会因为你没有选择OR而降低. 
   在下面的例子中, LOC_ID 和REGION上都建有索引.
高效:
   SELECT LOC_ID , LOC_DESC , REGION
   FROM LOCATION
   WHERE LOC_ID = 10
   UNION
   SELECT LOC_ID , LOC_DESC , REGION
   FROM LOCATION
   WHERE REGION = “MELBOURNE”

低效:
   SELECT LOC_ID , LOC_DESC , REGION
   FROM LOCATION
   WHERE LOC_ID = 10 OR REGION = “MELBOURNE”

如果你坚持要用OR, 那就需要返回记录最少的索引列写在最前面.

49.       优化GROUP BY
提高GROUP BY 语句的效率, 可以通过将不需要的记录在GROUP BY 之前过滤掉.下面两个查询返回相同结果但第二个明显就快了许多.
低效:
   SELECT JOB , AVG(SAL)
   FROM EMP
   GROUP JOB 
   HAVING JOB = ‘PRESIDENT’
   OR JOB = ‘MANAGER’
 高效:
   SELECT JOB , AVG(SAL)
   FROM EMP
   WHERE JOB = ‘PRESIDENT’
   OR JOB = ‘MANAGER’
   GROUP JOB

分享到:
评论

相关推荐

    Oracle SQL规范与优化注意事项

    Oracle SQL规范与优化注意事项 指导开发者写出高效的SQL语句

    sql 优化语句的几个常用注意事项

    保证数据库性能的sql语句写法,注意事项,使得我们在书写时避免使数据库低效率的执行命令

    SQL语法优化策略 、T-SQL编程注意事项

    SQL语法优化策略 、T-SQL编程注意事项SQL语法优化策略 、T-SQL编程注意事项SQL语法优化策略 、T-SQL编程注意事项SQL语法优化策略 、T-SQL编程注意事项SQL语法优化策略 、T-SQL编程注意事项

    优化sql语句执行效率几点注意事项

    优化sql语句执行效率几点需要注意的事项

    SQL优化原则

    SQL优化原则,及其注意事项

    sql优化文档

    整理好了的相关sql优化要注意的事项,2007文档

    SQL查询优化

    SQL查询优化,SQL查询优化常用技巧以及注意事项

    SQL语句的优化及SQL 性能调整原则

    SQL 编写注意事项 SQL语句的编写原则

    SQL执行过程和优化

    oracle SQL执行过程和优化 索引分类 索引注意事项 执行过程

    Oracle sql优化技术小结

    Oracle sql优化的一些常用方法与注意事项

    ORACLE+SQL效率优化.rar

    ORACLE+SQL效率优化.rar,详细讲解oracle中sql语句效率优化方法和注意事项。

    SQL优化总结,,PPT文档

    很基础的SQL优化需要注意的事项,优化的方法。浅显易懂,挺不错的。

    mysql百万数据性能优化注意事项.txt

    mysql百万数据性能优化注意事项,日常性能优化,sql优化,索引优化。

    SQLServer性能优化解析说明

    SQLServer性能优化解析,详细描述如何对SQLServer性能方面的一些注意事项,和值得优化的地方。

    SQL查询效率注意事项小结

    就可以减少解析的时间并减少那些由Column歧义引起的语法错误 四、少做重复工作 控制同一语句的多次执行,特别是一些基础数据的多次执行是很多程序员很少注意的 减少多次的数据转换 杜绝不必要的子查询和连接表,子...

    oracle_sql优化

    介绍在书写sql语句的时候要注意的一些事项,怎么才能写出高效的sql语句.

    SQL优化总结.docx

    自己项目中总结的一些SQL优化方法,以及需要注意的信息事项的,本来是作为自己的备忘录来使用的,分享下,大家学习学习。

    SQL数据库性能优化相关的注意事项总结

    近有初入行的朋友,向我询问公共测试用例的生成方法。其实我也才做测试没多久,关于公共测试用例这块的也不是像网友想象中那样有深刻的体会。下面我自己的体会做点阐述,也当总结一下几个月前工作过程中的总结吧。...

    Oracle数据库SQL语句优化策略

    基本的Sql编写注意事项 尽量少用IN操作符,基本上所有的IN操作符都可以用EXISTS代替 用IN写出来的SQL的优点是比较容易写及清晰易懂,但是用IN的SQL性能总是比较低的,从ORACLE执行的步骤来分析用IN的SQL与不用IN的...

    SQLSERVER性能优化综述

    一个系统的性能的提高,不单单是试运行或者维护阶段的性能调优的任务,也不单单是开发阶段的事情,而是在整个软件生命周期都...所以本文按照软件生命周期的不同阶段来总结数据库性能优化相关的注意事项..............

Global site tag (gtag.js) - Google Analytics