Oracle语句优化30个规则详解(二)

 

  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只在删除全表适用,TRUNCATEDDL不是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 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子句中)

  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)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值