ORACLE 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 作为基础表 ( 不佳的方法 )

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;

** 注意 : **

在 SQLPlus , SQLForms 和 Pro*C 中重新设置 ARRAYSIZE 参数 , 可以增加每次数据库访问的检索数据量 , 建议值为 200

(待续)

Published At
Categories with 数据库类
comments powered by Disqus