oracle sql优化

  Oracle 语句优化

1.        ORACLE 的解析器按照从右到左的顺序处理 FROM 子句中的表名,因此 FROM 子句中写在最后的表(基础表  driving table)将被最先处理。在 FROM 子句中包含多个表的情况下,你必须选择记录条数最少的表作为基础表。

例如:

              TAB1 16384 条记录

             TAB2 1 条记录

           选择 TAB2作为基础表  (最好的方法)

           select count(*) from tab1tab2 执行时间 0.96

           选择 TAB2作为基础表  (不佳的方法)

           select count(*) from tab2tab1 执行时间 26.09

2.        ORACLE 采用自下而上的顺序解析 WHERE 子句,根据这个原理,表之间的连接必须写在其他 WHERE 条件之前,那些可以过滤掉最大数量记录的条件必须写在 WHERE 子句的末尾。

例如:

    (低效,执行时间 156.3)

    SELECT …  

    FROM EMP E  

    WHERE SAL > 50000  

    AND JOB = ‘MANAGER’  

    AND 25 < (SELECT COUNT(*) FROM EMP  

    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’;

3.        SELECT子句中避免使用’*’

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

4.        使用DECODE 函数来减少处理时间, 使用 DECODE 函数可以避免重复扫描相同记录或重复连接相同的表。

5.        删除重复记录

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

DELETE FROM EMP E

WHERE E.ROWID > (SELECT MIN(X.ROWID)  

                         FROM EMP X

                         WHERE X.EMP_NO = E.EMP_NO);

6.        计算记录条数

和一般的观点相反,count(*) count(1)稍快,当然如果可以通过索引检索,对索

引列的计数仍旧是最快的。例如 COUNT(EMPNO)

7.        WHERE子句替换HAVING子句

避免使用 HAVING子句,HAVING 只会在检索出所有记录之后才对结果集进行过滤。 

这个处理需要排序,总计等操作。如果能通过 WHERE 子句限制记录的数目,那就能减少这方面的开销。

例如:

低效

SELECT REGIONAVG(LOG_SIZE)  

   FROM LOCATION  

   GROUP BY REGION  

   HAVING REGION REGION != ‘SYDNEY’

   AND REGION != ‘PERTH’

高效

SELECT REGIONAVG(LOG_SIZE)  

FROM LOCATION  

WHERE REGION REGION != ‘SYDNEY’  

AND REGION != ‘PERTH’  

GROUP BY REGION

HAVING 中的条件一般用于对一些集合函数的比较,如 COUNT() 等等。除此而外,一般的条件应该写在 WHERE 子句中。

8.        减少对表的查询

在含有子查询的 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)

9.        使用表的别名ALIAS

当在 SQL语句中连接多个表时,请使用表的别名并把别名前缀于每个 Column上。这

样一来,就可以减少解析的时间并减少那些由 Column歧义引起的语法错误。

(Column歧义指的是由于 SQL中不同的表具有相同的 Column名,当 SQL语句中出现这个 Column时,SQL解析器无法判断这个 Column的归属)

10.    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’)

11.    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’);  

12.    用表连接替换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’ ;

13.    EXISTS替换DISTINCT

当提交一个包含一对多表信息(比如部门表和雇员表)的查询时,避免在 SELECT 子句

中使用 DISTINCT. 一般可以考虑用 EXIST 替换

例如:

低效:

    SELECT DISTINCT DEPT_NO,DEPT_N

    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);

14.    避免在索引列上使用计算

WHERE 子句中,如果索引列是函数的一部分。优化器将不使用索引而使用全表扫描。 

举例:

低效:

   SELECT …  

   FROM DEPT  

   WHERE SAL * 12 > 25000;

高效:

   SELECT …  

   FROM DEPT  

   WHERE SAL > 25000/12;

 :这是一个非常实用的规则,请务必牢记

15.    避免在索引列上使用NOT

通常,我们要避免在索引列上使用 NOTNOT 会产生在和在索引列上使用函数相同

的影响。当ORACLE“遇到”NOT,他就会停止使用索引转而执行全表扫描。

举例:

低效:  (这里,不使用索引)

    SELECT …  

    FROM DEPT  

    WHERE DEPT_CODE NOT = 0;

高效: (这里,使用了索引)

    SELECT …  

    FROM DEPT  

    WHERE DEPT_CODE > 0;

16.    >=替代>

如果 DEPTNO 上有一个索引,

高效:

    SELECT *  

    FROM EMP  

    WHERE DEPTNO >=4

低效:

    SELECT *  

    FROM EMP  

    WHERE DEPTNO >3

两者的区别在于,前者 DBMS将直接跳到第一个 DEPT 等于 4的记录而后者将首先

定位到 DEPTNO=3的记录并且向前扫描到第一个 DEPT 大于 3的记录。

17.    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那就需要返回记录最少的索引列写在最前面。

18.    IN来替换OR

下面的查询可以被更有效率的语句替换:

低效:

    SELECT…  

    FROM LOCATION  

    WHERE LOC_ID = 10  

    OR LOC_ID = 20  

    OR LOC_ID = 30

高效:

    SELECT…  

    FROM LOCATION  

    WHERE LOC_IN IN (102030);

:这是一条简单易记的规则,但是实际的执行效果还须检验,在 ORACLE8i 下,两者

的执行路径似乎是相同的。

19.    避免在索引列上使用IS NULLIS NOT NULL

避免在索引中使用任何可以为空的列,ORACLE 将无法使用该索引。对于单列索引,

如果列包含空值,索引中将不存在此记录。对于复合索引,如果每个列都为空,索引中同样不存在此记录。  如果至少有一个列不为空,则记录存在于索引中。

举例:

如果唯一性索引建立在表的 A列和 B 列上,  并且表中存在一条记录的 A B 值为(123null)   ORACLE 将不接受下一条具有相同 AB (123null)的记录(插入)然而如果所有的索引列都为空, ORACLE 将认为整个键值为空而空不等于空。  因此你可以插入 1000条具有相同键值的记录,当然它们都是空! 因为空值不存在于索引列中,所以 WHERE 子句中对索引列进行空值比较将使ORACLE 停用该索引。

举例:

低效:(索引失效)

    SELECT …  

    FROM DEPARTMENT       WHERE DEPT_CODE IS NOT NULL;

高效:(索引有效)

    SELECT …  

    FROM DEPARTMENT  

    WHERE DEPT_CODE >=0;

20.    UNION ALL替换 UNION( 如果有可能的话)

SQL语句需要 UNION两个查询结果集合时,这两个结果集合会以 UNION-ALL的方式被合并,然后在输出最终结果前进行排序。如果用 UNION ALL替代 UNION这样排序就不是必要了。效率就会因此得到提高。

举例:

低效:

    SELECT ACCT_NUM BALANCE_AMT  

    FROM DEBIT_TRANSACTIONS  

    WHERE TRAN_DATE = ‘31-DEC-95’  

    UNION  

    SELECT ACCT_NUM BALANCE_AMT  

    FROM DEBIT_TRANSACTIONS  

    WHERE TRAN_DATE = ‘31-DEC-95’

高效:

    SELECT ACCT_NUM BALANCE_AMT  

    FROM DEBIT_TRANSACTIONS  

    WHERE TRAN_DATE = ‘31-DEC-95’   

    UNION ALL  

    SELECT ACCT_NUM BALANCE_AMT  

    FROM DEBIT_TRANSACTIONS  

    WHERE TRAN_DATE = ‘31-DEC-95’

:需要注意的是,UNION ALL 将重复输出两个结果集合中相同记录。因此各位还是

要从业务需求分析使用 UNION ALL 的可行性。UNION 将对结果集合排序,这个操作会使用到 SORT_AREA_SIZE这块内存。对于这块内存的优化也是相当重要的。下面的 SQL可以用来查询排序的消耗量

    Select substrname125  "Sort Area Name"

      substrvalue115    "Value"  

      from v$sysstat  

      where name like 'sort%'

21.    优化GROUP BY

提高 GROUP BY 语句的效率,  可以通过将不需要的记录在 GROUP BY 之前过滤掉。下面两个查询返回相同结果但第二个明显就快了许多。

低效:

    SELECT JOB AVG(SAL)  

    FROM EMP  

    GROUP by JOB  

    HAVING JOB = ‘PRESIDENT’  

    OR JOB = ‘MANAGER’

高效:

    SELECT JOB AVG(SAL)  

    FROM EMP  

    WHERE JOB = ‘PRESIDENT’      

OR JOB = ‘MANAGER’GROUP by JOB

 

你可能感兴趣的:(Oracle SQL)