SQL性能优化系列(一)

1、合适的oracle优化器 (rule、cost、choose)

 

2、访问表的方式

a、全表扫描

b、rowid访问  rowid记录的数据的物理地址信息,而oracle采用索引实现数据及其存放物理位置rowid之间的关联,故基于该些索引的查询就可以提高性能

 

3、共享sql     满足一下3个条件

a、字符级比较 : 被执行语句必须和共享池中语句完全相同

b、两语句所指对象相同

c、两语句须使用相同名字的绑定变量

 

4、有效率的表名顺序 (基于rule优化器才有效)

oracle解析器从右到左顺序处理from中的表名,因此最右边必须选择记录数最少的表(基础表),oracle处理多表时,先扫表基础表对其进行排序,然后继续第二个表,最后将第二个表检索的记录与第一个表中合适的记录进行合并

 

5、where子句中条件的连接顺序

oracle采用自下而上解析where子句,因此,表间连接必须写在其他where条件前,那些可以过滤掉最大数量记录的条件必须写在where子句末尾

 

6、select避免使用' * '

解析过程中,oracle会将*以此转换成所有列名,该工作通过查询数据字典完成,将耗费时间

 

7、减少访问DB的次数

执行SQL时,ORACLE做了许多工作:解析sql、估算索引利用率、绑定变量、读数据块等,因此减少访问DB次数,可减少DB的工作量

 

8、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,0)) D0020_SAL,

SUM(DECODE(DEPT_NO,0030,SAL,0)) D0030_SAL

FROM EMP WHERE ENAME LIKE ‘SMITH%'

 

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

合并成为:

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

通常情况,回滚段用来存放可恢复信息,若没有COMMIT事务,ORACLE将数据恢复到执行删除命令前的状态

而TRUNCATE不会产生任何REDO信息,回滚段也不再存放任何可恢复信息,数据不能恢复,故资源很少调用,执行时间短

 

12、尽量多使用COMMIT    会及时释放资源,故需求会减少,性能也能得到提升(同时也要考虑对事务完整性的影响)

释放的资源:

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

b、程序SQL获得的锁

c、redo log buffer 中的空间

d、管理上述3种的内部花费


13、计算记录条数

COUNT(*)  

COUNT(1) 

COUNT(索引列)   最快

 

14、Where子句替换HAVING子句

HAVING只会在检索出所有记录之后才对结果集进行过滤,这里存在排序,总计等操作。(HAVING中的条件一般用于对集合函数的比较,如count()等,而一般的条件应写入WHERE子句中)

WHERE子句会限制记录的数目,以减少上述方面的开销

SELECT REGION,AVG(LOG_SIZE)

FROM LOCATION

GROUP BY REGION

HAVING REGION != ‘SYDNEY'

AND REGION != ‘PERTH'


高效

SELECT REGION,AVG(LOG_SIZE)

FROM LOCATION

WHERE REGION != ‘SYDNEY'

AND REGION != ‘PERTH'

GROUP BY REGION

 

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 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;                                     //使用游标返回指定某HIST_TYPE的记录的TYPE_DESC字段值


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;                               //使用游标返回指定某EMPNO的记录的ENAME字段值


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;     

 

17、使用表的别名Alias

当SQL语句连接多表时,使用别名并将其前缀于每个Column上,可以减少解析时间、减少那些由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')

 

19、用NOT EXISTS替代NOT IN

在子查询中,NOT IN子句会执行一个内部排序和合并,无论何种情况下,NOT IN是最低效(因为它执行了一个全表遍历)

可以改用外连接或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');    

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值