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

  2.        访问 Table 的方式

  ORACLE 采用两种访问表中记录的方式 :

a.        全表扫描

            全表扫描就是顺序地访问表中每条记录 . ORACLE 采用一次读入多个数据块 (database block) 的方式优化全表扫描 .

    b.        通过 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.       字符级的比较 :

当前被执行的语句和共享池中的语句必须完全相同 .

      例如 :

          SELECT * FROM EMP;

      和下列每一个都不同

          SELECT * from EMP;

          Select * From Emp;

          SELECT      *     FROM EMP;

B.       两个语句所指的对象必须完全相同 :

例如 :

   用户      对象名            如何访问

Jack       sal_limit          private synonym

             Work_city      public synonym

             Plant_detail     public synonym

Jill         sal_limit          private synonym

             Work_city      public synonym

             Plant_detail     table owner

     考虑一下下列 SQL 语句能否在这两个用户之间共享 .

  SQL

能否共享

原因

select max(sal_cap) from sal_limit;

不能

每个用户都有一个 private synonym - sal_limit , 它们是不同的对 象

select count(*0 from work_city where sdesc like 'NEW%';

两个用户访问相同的对象 public synonym - work_city

select a.sdesc,b.location from work_city a , plant_detail b where a.city_id = b.city_id

不能

用户 jack 通过 private synonym 访问 plant_detail jill 是表的所有者 , 对象不同 .

  C.       两个 SQL 语句中必须使用相同的名字的绑定变量 (bind variables)

例如:第一组的两个 SQL 语句是相同的 ( 可以共享 ), 而第二组中的两个语句是不同的 ( 即使在运行时 , 赋于不同的绑定变量相同的值 )

a.

select pin , name from people where pin = :blk1.pin;

select pin , name from people where pin = :blk1.pin;

b.

select pin , name from people where pin = :blk1.ot_ind ;

select pin , name from people where pin = :blk1.ov_ind ;

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

例如 :       TAB1 16,384 条记录

            TAB2 1      条记 录

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

     select count(*) from tab1,tab2   执行时间 0.96

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

     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

             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

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

11.        TRUNCATE 替代 DELETE

当删除表中的记录时 , 在通常情况下 , 回滚段 (rollback segments ) 用来存放可以被恢复的信息 . 如果你没有 COMMIT 事务 ,ORACLE 会将数据恢复到删除之前的状态 ( 准确地说是恢复到执行删除命令之前的状况 )

而当运用 TRUNCATE , 回滚段不再存放任何可被恢复的信息 . 当命令运行后 , 数据不能被恢复 . 因此很少的资源被调用 , 执行时间也会很短 .

  ( 译者按 : TRUNCATE 只在删除全表适用 ,TRUNCATE DDL 不是 DML)

12.        尽量多使用 COMMIT

只要有可能 , 在程序中尽量多使用 COMMIT, 这样程序的性能得到提高 , 需求也会因为 COMMIT 所释放的资源而减少 :

  COMMIT 所释放的资源 :

a.        回滚段上用于恢复数据的信息 .

b.        被程序语句获得的锁

c.        redo log buffer 中的空间

d.        ORACLE 为管理上述 3 种资源中的内部花费

  ( 译者按 : 在使用 COMMIT 时必须要注意到事务的完整性 , 现实中效率和事务完整性往往是鱼和熊掌不可得兼 )

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;

   16.        通过内部函数提高 SQL 效率 .

      SELECT H.EMPNO,E.ENAME,H.HIST_TYPE,T.TYPE_DESC,COUNT(*)

     FROM HISTORY_TYPE T,EMP E,EMP_HISTORY H

     WHERE H.EMPNO = E.EMPNO

AND H.HIST_TYPE = T.HIST_TYPE

GROUP BY H.EMPNO,E.ENAME,H.HIST_TYPE,T.TYPE_DESC;

通过调用下面的函数可以提高效率 .

FUNCTION LOOKUP_HIST_TYPE(TYP IN NUMBER) RETURN VARCHAR2

AS

    TDESC VARCHAR2(30);

    CURSOR C1 IS 

        SELECT TYPE_DESC

        FROM HISTORY_TYPE

        WHERE HIST_TYPE = TYP;

BEGIN

    OPEN C1;

    FETCH C1 INTO TDESC;

    CLOSE C1;

    RETURN (NVL(TDESC,’?’));

END;

 

FUNCTION LOOKUP_EMP(EMP IN NUMBER) RETURN VARCHAR2

AS

    ENAME VARCHAR2(30);

    CURSOR C1 IS 

        SELECT ENAME

        FROM EMP

        WHERE EMPNO=EMP;

BEGIN

    OPEN C1;

    FETCH C1 INTO ENAME;

    CLOSE C1;

    RETURN (NVL(ENAME,’?’));

END;

 

SELECT H.EMPNO,LOOKUP_EMP(H.EMPNO),

H.HIST_TYPE,LOOKUP_HIST_TYPE(H.HIST_TYPE) ,COUNT(*)

FROM EMP_HISTORY H

GROUP BY H.EMPNO , H.HIST_TYPE;

  ( 译者按 : 经常在论坛中看到如 能不能用一个 SQL 写出 ….’ 的贴子 , 殊不知复杂的 SQL 往往牺牲了执行效率 . 能够掌握上面的运用函数解决问题的方法在实际工作中是非常有意义的 )

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

  22.        识别 低效执行 SQL 语句

用下列 SQL 工具找出低效 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 4 DESC;

     ( 译者按 : 虽然目前各种关于 SQL 优化的图形化工具层出不穷 , 但是写出自己的 SQL 工具来解决问题始终是一个最好的方法 )

  23.        使用 TKPROF 工具来查询 SQL 性能状态

  SQL trace 工具收集正在执行的 SQL 的性能状态数据并记录到一个跟踪文件中 . 这个跟踪文件提供了许多有用的信息 , 例如解析次数 . 执行次数 ,CPU 使用时间等 . 这些数据将可以用来优化你的系统 .

设置 SQL TRACE 在会话级别 : 有效

   ALTER SESSION SET SQL_TRACE TRUE

设置 SQL TRACE 在整个数据库有效仿 , 你必须将 SQL_TRACE 参数在 init.ora 中设为 TRUE, USER_DUMP_DEST 参数说明了生成跟踪文件的目录

  ( 译者按 : 这一节中 , 作者并没有提到 TKPROF 的用法 , SQL TRACE 的用法也不够准确 , 设置 SQL TRACE 首先要在 init.ora 中设定 TIMED_STATISTICS, 这样才能得到那些重要的时间状态 . 生成的 trace 文件是不可读的 , 所以要用 TKPROF 工具对其进行转换 ,TKPROF 有许多执行参数 . 大家可以参考 ORACLE 手册来了解具体的配置 . )

24.        EXPLAIN PLAN 分析 SQL 语句

 EXPLAIN PLAN 是一个很好的分析 SQL 语句的工具 , 它甚至可以在不执行 SQL 的情况下分析语句 . 通过分析 , 我们就可以知道 ORACLE 是怎么样连接表 , 使用什么方式扫描表 ( 索引扫描或全表扫描 ) 以及使用到的索引名称 .

你需要按照从里到外 , 从上到下的次序解读分析的结果 . EXPLAIN PLAN 分析的结果是用缩进的格式排列的 , 最内部的操作将被最先解读 , 如果两个操作处于同一层中 , 带有最小操作号的将被首先执行 .

NESTED LOOP 是少数不按照上述规则处理的操作 , 正确的执行路径是检查对 NESTED LOOP 提供数据的操作 , 其中操作号最小的将被最先处理 .

译者按 :

  通过实践 , 感到还是用 SQLPLUS 中的 SET TRACE 功能比较方便 .

举例 :

 SQL> list

  1  SELECT *

  2  FROM dept, emp

  3* WHERE emp.deptno = dept.deptno

SQL> set autotrace traceonly /*traceonly 可以不显示执行结果 */

SQL> /

14 rows selected.

Execution Plan

----------------------------------------------------------

   0      SELECT STATEMENT Optimizer=CHOOSE

   1    0   NESTED LOOPS

   2    1     TABLE ACCESS (FULL) OF 'EMP'

   3    1     TABLE ACCESS (BY INDEX ROWID) OF 'DEPT'

   4    3       INDEX (UNIQUE SCAN) OF 'PK_DEPT' (UNIQUE)

 

Statistics

----------------------------------------------------------

          0  recursive calls

          2  db block gets

         30  consistent gets

          0  physical reads

          0  redo size

       2598  bytes sent via SQL*Net to client

        503  bytes received via SQL*Net from client

          2  SQL*Net roundtrips to/from client

          0  sorts (memory)

          0  sorts (disk)

         14  rows processed

通过以上分析 , 可以得出实际的执行步骤是 :

1.        TABLE ACCESS (FULL) OF 'EMP'

2.        INDEX (UNIQUE SCAN) OF 'PK_DEPT' (UNIQUE)

3.        TABLE ACCESS (BY INDEX ROWID) OF 'DEPT'

4.        NESTED LOOPS (JOINING 1 AND 3)

: 目前许多第三方的工具如 TOAD ORACLE 本身提供的工具如 OMS SQL Analyze 都提供了极其方便的 EXPLAIN PLAN 工具 . 也许喜欢图形化界面的朋友们可以选用它们 .

25.        用索引提高效率

 

索引是表的一个概念部分 , 用来提高检索数据的效率 . 实际上 ,ORACLE 使用了一个复杂 的自平衡 B-tree 结构 . 通常 , 通过索引查询数据比全表扫描要快 . ORACLE 找出执行查询和 Update 语句的最佳路径时 , ORACLE 优化器将使用索引 . 同样在联结多个表时使用索引 也可以提高效率 . 另一个使用索引的好处是 , 它提供了主键 (primary key) 的唯一性验证 .

除了那些 LONG LONG RAW 数据类型 , 你可以索引几乎所有的列 . 通常 , 在大型表中使用索引特别有效 . 当然 , 你也会发现 , 在扫描小表时 , 使用索引同样能提高效率 .

虽然使用索引能得到查询效率 的提高 , 但是我们也必须注意到它的代价 . 索引需要空间来

存储 , 也需要定期维护 , 每当有记录在表中增减或索引 列被修改时 , 索引本身也会被修改 . 这意味着每条记录的 INSERT , DELETE , UPDATE 将为此多付出 4 , 5 次的磁盘 I/O . 因为索引需要额 外的存储空间和处理 , 那些不必要的索引反而会使查询反应时间变慢 .

 

译者按 :

定期的重构索引是有必要的 .

ALTER INDEX <INDEXNAME> REBUILD <TABLESPACENAME>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值