ORACLE数据库优化总结.docx

上传人:b****5 文档编号:4662042 上传时间:2022-12-07 格式:DOCX 页数:39 大小:41.51KB
下载 相关 举报
ORACLE数据库优化总结.docx_第1页
第1页 / 共39页
ORACLE数据库优化总结.docx_第2页
第2页 / 共39页
ORACLE数据库优化总结.docx_第3页
第3页 / 共39页
ORACLE数据库优化总结.docx_第4页
第4页 / 共39页
ORACLE数据库优化总结.docx_第5页
第5页 / 共39页
点击查看更多>>
下载资源
资源描述

ORACLE数据库优化总结.docx

《ORACLE数据库优化总结.docx》由会员分享,可在线阅读,更多相关《ORACLE数据库优化总结.docx(39页珍藏版)》请在冰豆网上搜索。

ORACLE数据库优化总结.docx

ORACLE数据库优化总结

ORACLE数据库

SQL语句编写优化总结

 

ORACLE3

1、选用适合的ORACLE优化器3

2、访问Table的方式3

3、共享SQL语句4

4、选择最有效率的表名顺序(只在基于规则的优化器中有效)5

5、WHERE子句中的连接顺序.6

6、SELECT子句中避免使用‘*’6

7、减少访问数据库的次数6

8、使用DECODE函数来减少处理时间7

9、整合简单,无关联的数据库访问8

10、 删除重复记录8

11、用TRUNCATE替代DELETE9

12、尽量多使用COMMIT9

13、计算记录条数9

14、用Where子句替换HAVING子句9

15、减少对表的查询10

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

11

17、使用表的别名(Alias)12

18、用EXISTS替代IN12

19、用NOTEXISTS替代NOTIN12

20、用表连接替换EXISTS13

21、用EXISTS替换DISTINCT13

22、识别’低效执行’的SQL语句14

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

24、用EXPLAINPLAN分析SQL语句14

25、用索引提高效率15

26、索引的操作16

27、基础表的选择17

28、多个平等的索引18

29、等式比较和范围比较18

30、不明确的索引等级19

31、强制索引失效20

32。

避免在索引列上使用计算20

33、自动选择索引21

34、避免在索引列上使用NOT21

35。

用>=替代>22

36、用UNION替换OR(适用于索引列)22

37、用IN来替换OR25

38、避免在索引列上使用ISNULL和ISNOTNULL25

39、总是使用索引的第一个列26

40、ORACLE内部操作26

41、用UNION-ALL替换UNION(如果有可能的话)27

42、使用提示(Hints)28

43、用WHERE替代ORDERBY29

44、避免改变索引列的类型。

29

45、需要当心的WHERE子句30

46、连接多个扫描31

47、CBO下使用更具选择性的索引32

48、避免使用耗费资源的操作32

49、优化GROUPBY32

50、使用显式的游标(CURSORS)33

51、分离表和索引33

ORACLE

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-BasedOptimizer),必须经常运行analyze命令(相当于DB2中的runstats),以增加数据库中的对象统计信息(objectstatistics)的准确性。

如果数据库的优化器模式设置为选择性(CHOOSE),那么实际的优化器模式将和是否运行过analyze命令有关,如果table已经被analyze过,优化器模式将自动成为CBO,反之,数据库将采用RULE形式的优化器。

在缺省情况下,ORACLE采用的是CHOOSE优化器,如果你的数据库设计的不好,也不经常统计信息,最好用RULE模式,因为在CHOOSE模式下会产生很多全表扫描(FULLSCAN),数据库中最忌讳的就是对大表进行FULLSCAN,但是必须情况下除外。

2、访问Table的方式

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

a、全表扫描

全表扫描就是顺序地访问表中每条记录。

ORACLE采用一次读入多个数据块(databaseblock)的方式优化全表扫描。

对于大表尽量避免产生全表扫描。

b、通过ROWID访问表

可以采用基于ROWID的访问方式访问表记录,提高访问效率。

ROWID包含了表中记录的物理位置信息,ORACLE采用索引(INDEX)实现了数据和存放数据的物理位置(ROWID)之间的联系。

通常索引提供了快速访问ROWID的方法,因此那些基于索引列的查询就可以得到性能上的提高。

3、共享SQL语句

为了不重复解析相同的SQL语句,在第一次解析之后,ORACLE将SQL语句存放在内存中。

这块位于系统全局区域SGA(systemglobalarea)的共享池(sharedbufferpool)中的内存可以被所有的数据库用户共享。

因此,当你执行一个SQL语句(有时被称为一个游标)时,如果它和之前的执行过的语句完全相同,ORACLE就能很快获得已经被解析的语句以及最好的执行路径。

ORACLE的这个功能大大地提高了SQL的执行性能并节省了内存的使用。

可惜的是ORACLE只对简单的表提供高速缓冲(cachebuffering),这个功能并不适用于多表连接查询。

数据库管理员必须在init.ora中为这个区域设置合适的参数,当这个内存区域越大,就可以保留更多的语句,当然被共享的可能性也就越大了。

当你向ORACLE提交一个SQL语句,ORACLE会首先在这块内存中查找相同的语句。

这里需要注明的是,ORACLE对两者采取的是一种严格匹配,要达成共享,SQL语句必须

完全相同(包括空格,换行等)。

共享的语句必须满足三个条件:

 A、字符级的比较:

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

例如:

SELECT*FROMEMP;

和下列每一个都不同

SELECT*fromEMP;

Select*FromEmp;

SELECT*FROMEMP;

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

例如

用户对象名如何访问

Jacksal_limitprivatesynonym

Work_citypublicsynonym

Plant_detailpublicsynonym

Jillsal_limitprivatesynonym

Work_citypublicsynonym

Plant_detailtableowner

 SQL语句能否在这两个用户之间共享,如下表

SQL

能否共享

原因

selectmax(sal_cap)fromsal_limit;

不能

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

selectcount(*)fromwork_citywheresdesclike'NEW%';

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

selecta.sdesc,b.locationfromwork_citya,plant_detailbwherea.city_id=b.city_id

不能

用户jack通过publicsynonym访问plant_detail而jill是表的所有者,对象不同。

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

例如:

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

a.

selectpin,namefrompeoplewherepin=:

blk1.pin;

selectpin,namefrompeoplewherepin=:

blk1.pin;

b.

selectpin,namefrompeoplewherepin=:

blk1.ot_ind;

selectpin,namefrompeoplewherepin=:

blk1.ov_ind;

4、选择最有效率的表名顺序(只在基于规则的优化器中有效)

ORACLE的解析器按照从右到左的顺序处理FROM子句中的表名,因此FROM子句中写在最后的表(基础表drivingtable)将被最先处理。

在FROM子句中包含多个表的情况下,你必须选择记录条数最少的表作为基础表。

当ORACLE处理多个表时,会运用排序及合并的方式连接它们。

首先,扫描第一个表(FROM子句中最后的那个表)并对记录进行派序,然后扫描第二个表(FROM子句中最后第二个表),最后将所有从第二个表中检索出的记录与第一个表中合适记录进行合并。

例如:

表TAB116384条记录

表TAB21条记录

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

selectcount(*)fromtab1,tab2

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

selectcount(*)fromtab2,tab1

两者相差的时间可能在几十倍甚至几百倍。

如果有3个以上的表连接查询,那就需要选择交叉表(intersectiontable)作为基础表,交叉表是指那个被其他表所引用的表。

例如:

EMP表描述了LOCATION表和CATEGORY表的交集。

SELECT*

FROMLOCATIONL,

CATEGORYC,

EMPE

WHEREE.EMP_NOBETWEEN1000AND2000

ANDE.CAT_NO=C.CAT_NO

ANDE.LOCN=L.LOCN

 将比下面SQL更有效率

SELECT*

FROMEMPE,

LOCATIONL,

CATEGORYC

WHEREE.CAT_NO=C.CAT_NO

ANDE.LOCN=L.LOCN

ANDE.EMP_NOBETWEEN1000AND2000

5、WHERE子句中的连接顺序.

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

 例如:

(低效)

SELECT…

FROMEMPE

WHERESAL>50000

ANDJOB=‘MANAGER’

AND25<(SELECTCOUNT(*)FROMEMP

WHEREMGR=E.EMPNO);

(高效)

SELECT…

FROMEMPE

WHERE25<(SELECTCOUNT(*)FROMEMP

WHEREMGR=E.EMPNO)

ANDSAL>50000

ANDJOB=‘MANAGER’;

6、SELECT子句中避免使用‘*’

当你想在SELECT子句中列出所有的COLUMN时,使用动态SQL列引用‘*’是一个方便的方法。

不幸的是,这是一个非常低效的方法。

实际上,ORACLE在解析的过程中,会将‘*’依次转换成所有的列名,这个工作是通过查询数据字典完成的,这意味着将耗费更多的时间。

7、减少访问数据库的次数

当执行每条SQL语句时,ORACLE在内部执行了许多工作:

解析SQL语句,估算索引的利用率,绑定变量,读数据块等等。

由此可见,减少访问数据库的次数,就能实际上减少ORACLE的工作量。

例如

以下有三种方法可以检索出雇员号等于0342或0291的职员。

 方法1(最低效)

SELECTEMP_NAME,SALARY,GRADE

FROMEMP

WHEREEMP_NO=342;

SELECTEMP_NAME,SALARY,GRADE

FROMEMP

WHEREEMP_NO=291;

方法2(次低效)

DECLARE

CURSORC1(E_NONUMBER)IS

SELECTEMP_NAME,SALARY,GRADE

FROMEMP

WHEREEMP_NO=E_NO;

BEGIN

OPENC1(342);

FETCHC1INTO…,…,…;

OPENC1(291);

FETCHC1INTO…,…,…;

CLOSEC1;

END;

方法3(高效)

SELECTA.EMP_NAME,A.SALARY,A.GRADE,

B.EMP_NAME,B.SALARY,B.GRADE

FROMEMPA,EMPB

WHEREA.EMP_NO=342ANDB.EMP_NO=291;

 注意:

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

8、使用DECODE函数来减少处理时间

使用DECODE函数可以避免重复扫描相同记录或重复连接相同的表。

例如:

SELECTCOUNT(*),SUM(SAL)

FROMEMP

WHEREDEPT_NO=0020ANDENAMELIKE‘SMITH%’;

SELECTCOUNT(*),SUM(SAL)

FROMEMP

WHEREDEPT_NO=0030ANDENAMELIKE‘SMITH%’;

你可以用DECODE函数高效地得到相同结果

SELECTCOUNT(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

FROMEMPWHEREENAMELIKE‘SMITH%’;

类似的,DECODE函数也可以运用于GROUPBY和ORDERBY子句中。

9、整合简单,无关联的数据库访问

如果你有几个简单的数据库查询语句,你可以把它们整合到一个查询中(即使它们之间没有关系)

例如:

SELECTNAME

FROMEMP

WHEREEMP_NO=1234;

SELECTNAME

FROMDPT

WHEREDPT_NO=10;

SELECTNAME

FROMCAT

WHERECAT_TYPE=‘RD’;

上面的3个查询可以被合并成一个:

SELECTE.NAME,D.NAME,C.NAME

FROMCATC,DPTD,EMPE,DUALX

WHERENVL(‘X’,X.DUMMY)=NVL(‘X’,E.ROWID(+))

ANDNVL(‘X’,X..DUMMY)=NVL(‘X’,D.ROWID(+))

ANDNVL(‘X’,X..DUMMY)=NVL(‘X’,C.ROWID(+))

ANDE.EMP_NO(+)=1234

ANDD.DEPT_NO(+)=10

ANDC.CAT_TYPE(+)=‘RD’;

(虽然采取这种方法,效率得到提高,但是程序的可读性大大降低,所以最后实际采用哪种方式还是要权衡之间的利弊)

10、 删除重复记录

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

DELETEFROMEMPE

WHEREE.ROWID>(SELECTMIN(X.ROWID)

FROMEMPX

WHEREX.EMP_NO=E.EMP_NO);

11、用TRUNCATE替代DELETE

当删除表中的记录时,在通常情况下,回滚段(rollbacksegments)用来存放可以被恢复的信息。

如果你没有COMMIT事务,ORACLE会将数据恢复到删除之前的状态(准确地说是恢复到执行删除命令之前的状况)

而当运用TRUNCATE时,回滚段不再存放任何可被恢复的信息。

当命令运行后,数据不能被恢复。

因此很少的资源被调用,执行时间也会很短。

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

12、尽量多使用COMMIT

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

COMMIT所释放的资源:

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

B.被程序语句获得的锁

C.redologbuffer中的空间

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

(在使用COMMIT时必须要注意到事务的完整性,现实中效率和事务完整性往往是鱼和熊掌不可得兼,根据需求自己平衡)

13、计算记录条数

和一般的观点相反,count(*)比count

(1)稍快,当然如果可以通过索引检索,对索引列的计数仍旧是最快的。

例如COUNT(EMPNO),所以索引建立的合理性非常重要。

 (在网上,曾经对此有过相当热烈的讨论。

通过实际的测试,上述三种方法并没有显著的性能差别,但本人观点并不十分准确,因为这还涉及到很多oracle内部的优化机制,不同版本的oracle也不大相同)

14、用Where子句替换HAVING子句

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

这个处理需要排序,总计等操作。

如果能通过WHERE子句限制记录的数目,那就能减少这方面的开销。

 例如:

低效:

SELECTREGION,AVG(LOG_SIZE)

FROMLOCATION

GROUPBYREGION

HAVINGREGIONREGION!

=‘SYDNEY’

ANDREGION!

=‘PERTH’

 高效

SELECTREGION,AVG(LOG_SIZE)

FROMLOCATION

WHEREREGIONREGION!

=‘SYDNEY’

ANDREGION!

=‘PERTH’

GROUPBYREGION

(HAVING中的条件一般用于对一些集合函数的比较,如COUNT()等等。

除此而外,一般的条件应该写在WHERE子句中)

15、减少对表的查询

在含有子查询的SQL语句中,要特别注意减少对表的查询。

例如:

低效

SELECTTAB_NAME

FROMTABLES

WHERETAB_NAME=(SELECTTAB_NAME

FROMTAB_COLUMNS

WHEREVERSION=604)

AND DB_VER=(SELECTDB_VER

FROMTAB_COLUMNS

WHEREVERSION=604)

高效

SELECTTAB_NAME

FROMTABLES

WHERE(TAB_NAME,DB_VER)

=(SELECTTAB_NAME,DB_VER

FROMTAB_COLUMNS

WHEREVERSION=604)

Update多个Column例子:

低效:

UPDATEEMP

SETEMP_CAT=(SELECTMAX(CATEGORY)FROMEMP_CATEGORIES),

SAL_RANGE=(SELECTMAX(SAL_RANGE)FROMEMP_CATEGORIES)

WHEREEMP_DEPT=0020;

高效:

UPDATEEMP

SET(EMP_CAT,SAL_RANGE)

=(SELECTMAX(CATEGORY),MAX(SAL_RANGE)

FROMEMP_CATEGORIES)

WHEREEMP_DEPT=0020;

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

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

FROMHISTORY_TYPET,EMPE,EMP_HISTORYH

WHEREH.EMPNO=E.EMPNOANDH.HIST_TYPE=T.HIST_TYPE

GROUPBYH.EMPNO,E.ENAME,H.HIST_TYPE,T.TYPE_DESC;

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

FUNCTIONLOOKUP_HIST_TYPE(TYPINNUMBER)RETURNVARCHAR2

AS

TDESCVARCHAR2(30);

CURSORC1IS

SELECTTYPE_DESC

FROMHISTORY_TYPE

WHEREHIST_TYPE=TYP;

BEGIN

OPENC1;

FETCHC1INTOTDESC;

CLOSEC1;

RETURN(NVL(TDESC,’?

’));

END;

 

FUNCTIONLOOKUP_EMP(EMPINNUMBER)RETURNVARCHAR2

AS

ENAMEVARCHAR2(30);

CURSORC1IS

SELECTENAME

FROMEMP

WHEREEMPNO=EMP;

BEGIN

OPENC1;

FETCHC1INTOENAME;

CLOSEC1;

RETURN(NVL(ENAME,’?

’));

END;

 

SELECTH.EMPNO,LOOKUP_EMP(H.EMPNO),

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

FROMEMP_HISTORYH

GROUPBYH.EMPNO,H.HIST_TYPE;

(经常有人想’能不能用一个SQL写出…’的想法,殊不知复杂的SQL往往牺牲了执行效率,这在大数据量时往往是致命的。

能够掌握上面的运用函数解决问题的方法在实际工作中是非常有意义的)

17、使用表的别名(Alias)

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

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

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

18、用EXISTS替代IN

在许多基于基础表的查询中,为了满足一个条件,往往需要对另一个表进行联接。

在这种情况下,使用EXISTS(或NOTEXISTS)通常将提高查询的效率。

 低效:

SELECT*

FROMEMP(基础表)

WHEREEMPNO>0

ANDDEPTNOIN(SELECTDEPTNOFROMDEPTWHERELOC=‘MELB’)

高效:

S

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 人文社科 > 文化宗教

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1