Oracle优化器的选择
一、
,CcH&k z1x"}E24558279Oracle的优化器共有3种模式:
O7ho:~0qW9~%Z FW24558279RULE (基于规则RBO)、COST(基于成本CBO)、CHOOSE(基于选择)
2NJ&P|$^ S%C24558279
8n _iJ^H24558279RBO方式:优化器在分析SQL语句时,所遵循的是Oracle内部预定的一些规则。比如我们常见的,当一个where子句中的一列有索引时去走索引。
W#oK2y1Mqj24558279 ITPUB个人空间Q0c)MKC:qS
CBO方式:它是看语句的代价(Cost),这里的代价主要指Cpu和内存。优化器在判断是否用这种方式时,主要参照的是表及索引的统计信息。统计信息给出表的大小、有少行、每行的长度等信息。这些统计信息起初在库内是没有的,是做analyze命令后才出现的,很多的时侯过期统计信息会令优化器做出一个错误的执行计划,因此应及时更新这些信息。ITPUB个人空间T2^pxr J:yiHk k
ITPUB个人空间"bRL D6_
Choose:这个是Oracle的默认值。当一个表或索引有统计信息,则走CBO的方式,如果表或索引没有统计信息,表又不是特别的小,而且相应的列有索引时,那么就走索引,走RBO的方式.
二、
'u-iYq+_qw1WGm9w24558279程序中尽量使用绑定变量:避免或减少硬解析ITPUB个人空间qVw1VqY4]zP\
例如:ITPUB个人空间pQc.` {;Agw
select * from t1 where t1.a=1;ITPUB个人空间9vZ'HG.m|%v/y
select * from t1 where t1.a=2;ITPUB个人空间yf+ZN&Zrg%}
select * from t1 where t1.a=3;
select * from t1 where t1.a=1000000;
Gv.s,L2H0zKjQ24558279上面这个语句每执行一次就需要在SHARE POOL 硬解析一次,如果一百万户万并发查询,就要硬解析一百万次,消耗CPU和内存,如果业务量大,很可能导致共享池满,数据库宕机。ITPUB个人空间)Y1zv8v/~7i1C Q
如果绑定变量,则只需要硬解析一次,重复调用即可。
三、ITPUB个人空间G,\'R7ku)zJ#Q;f
Select 中尽量避免使用 "*"ITPUB个人空间Q$ALIB7L
这样的全字段访问,ORACLE在解析的过程中, 会将*依次转换成所有的列名,这个工作是通过查询数据字典完成的,这意味着将耗费更多的时间。只提取你所要使用的列;使用别名能够加快解析速度.
四、ITPUB个人空间6tD^o:s*x}
避免使用耗费资源的操作
C#`_8T*a5Z-z?:D24558279带有DISTINCT,UNION,MINUS,INTERSECT,ORDER BY的ITPUB个人空间q~*gu ]I8gR
SQL语句会启动SQL引擎执行耗费资源的排序(SORT)功能.
V;X{/bV+t5T24558279DISTINCT需要一次排序操作, 而其他的至少需要执行两次ITPUB个人空间t;eO3Ahb"?R*M
排序.
f"tGM0sa H24558279例如:一个UNION查询,其中每个查询都带有GROUP BY子句ITPUB个人空间 T}fss G1W
, GROUP BY会触发嵌入排序(NESTED SORT) ; 这样, 每个
%bo/HxP7b_24558279查询需要执行一次排序, 然后在执行UNION时, 又一个唯一ITPUB个人空间/@I/N*O)Ma3U;i6J
排序(SORT UNIQUE)操作被执行,而且它只能在前面的嵌入
3E[ CJP24558279排序结束后才能开始执行. 嵌入的排序的深度会大大影响查
A0c7iV4V.Y24558279询的效率.ITPUB个人空间7?+G1\G!zM'c
通常, 带有UNION, MINUS , INTERSECT的SQL语句都可以
;ut*d]#?&C24558279用其他方式重写.
五、ITPUB个人空间;DCE`q.w
用EXISTS替换DISTINCT
jQe7u|){ wY]24558279例如:ITPUB个人空间&B^OR#d7n:dsTgg%K
低效:
WT js;f}!\1M\+Q24558279SELECT DISTINCT DEPT_NO,DEPT_NAME
^'B3WBx(|4A"Y24558279FROM DEPT D,EMP EITPUB个人空间+NC Vnc x3L
WHERE D.DEPT_NO = E.DEPT_NO
-e D;q6`2H0OLEo24558279高效:ITPUB个人空间e#JT_'B
SELECT DEPT_NO,DEPT_NAMEITPUB个人空间)J.U|\2J2`5Rs bQrI
FROM DEPT DITPUB个人空间m C9`'Gj UV{
WHERE EXISTS ( SELECT ‘X’
;bag$Tk/k | k%j24558279FROM EMP EITPUB个人空间n^:Ew?
WHERE E.DEPT_NO = D.DEPT_NO);
六、
%k T7O8y%y"e&ds|24558279用UNION-ALL 替换UNION (允许的话)
w6Zo}6W24558279UNION-ALL 不去重,效率更高;UNION先以UNION-ALL的方式被合并, 然后进行排序。ITPUB个人空间U@Stm4gf
例如:
5m1s&FV;{W/Z24558279低效:
bGmBo2H&i!J \24558279SELECT ACCT_NUM, BALANCE_AMT
2P!f`6k*L2g24558279FROM DEBIT_TRANSACTIONS WHERE TRAN_DATE = ’31-DEC-95’
;}I!Fga I7H24558279UNIONITPUB个人空间q X)g:a*a5Qv
SELECT ACCT_NUM, BALANCE_AMT
{9B%V2ltx-g24558279FROM DEBIT_TRANSACTIONS WHERE TRAN_DATE = ’31-DEC-95;
N*^%?6cp7y24558279高效:ITPUB个人空间+ON6s&b.a/f5{Jg
SELECT ACCT_NUM, BALANCE_AMTITPUB个人空间#b+}tHsBO
FROM DEBIT_TRANSACTIONS WHERE TRAN_DATE = ’31-DEC-95’
F F8Xmw`)rP0HZ24558279UNION ALL
Nqx"G^8u-er24558279SELECT ACCT_NUM, BALANCE_AMTITPUB个人空间 aq5M?!GQ,R$l
FROM DEBIT_TRANSACTIONS WHERE TRAN_DATE = ’31-DEC-95;
七、
g|!_[qu24558279组合索引必须使用先导列ITPUB个人空间)rRqWwL\8|`^8u
如果索引是建立在多个列上, 只有在它的第一个列(先导列) 被where子句引用时,优化器才会选择使用该索引.
z4Gqf+|0a;]24558279
)XXx_R;tq6W$g24558279例如:
P8] xs N/c3b/aD24558279create table multiindexusage ( inda number , indb number , descr varchar2(10));ITPUB个人空间;x&g9O^/K7f&zZ
create index multindex on multiindexusage(inda,indb);ITPUB个人空间+pV1[gIRI,R
select * from multiindexusage where inda = 1; --走索引ITPUB个人空间h5]:U;^:D&le|
select * from multiindexusage where indb = 1; --不走索引
八、ITPUB个人空间g%@9H/I-w8o
WHERE条件中正确使用函数或者表达式ITPUB个人空间*T|)~W#Dv D0F%g
sql语句中,where条件中要正确使用函数或者表达式,不正确使用,会进行ITPUB个人空间/t)BjVsy1|
字段类型转换,字段上索引失效ITPUB个人空间s E%d{@|0w$l
例如:
8r5H%R1Av}v3{'_8v24558279下面语句会使索引失效,并且把数据库库中所有记录的log_time时间值转换
Y6y(?1ne9gk24558279为字符后,和条件中的字符串进行比较,走全表扫描,sql 执行效率大大降
?` JiZ/HL)LHi24558279低。ITPUB个人空间\dW4{^B$i@
select log_time from account_login where to_char(log_time,'yyyy-mmITPUB个人空间1A_]J-rSHyr
-dd hh24:mi:ss')='2009-04-01 00:00:00';ITPUB个人空间-EeB?5EF2y
正确写法,使用to_date函数
x;Q C!}R6ar+|24558279select log_time from account_login where log_time=to_date('2009-0
NoH@;CX~R/TL245582794-01 00:00:00','YYYY-MM-DD HH24:MI:SS');
九、ITPUB个人空间}'c6XF3@Po}+X.z
合理使用函数索引,提升查询速度
BqS2J$X4P.Y24558279
;i,j(rw+A24558279在项目的开发中,经常需要对外部传送如数据库中的数据,进行处理后,再将它们存放在数据库中
%u0U?ii]&G)I8FX24558279
A#k"l^|0{1~ N&d2n24558279例如:ITPUB个人空间sam4lU8jn `'Ig
select account from account_base where account=lower('AbcDe');
*f0xN5P$\/I i.X8BO24558279在account字段上创建索引,使用lower函数后,索引失效
BZx-G1KS24558279在account字段上创建函数索引lower(account),语句改写如下:
k)cyk(W'bR24558279select account from account_base where lower(account)=lower('AbcDe');ITPUB个人空间OJ _5?0c-U ^.}
这样查收走函数索引lower(account),会大大提升查询效率
十、ITPUB个人空间`D_zzY
带通配符(%)的like语句
'n(q` Ug"I(U8isBIT24558279last_name字段创建索引,下面语句Oracle不使用last_name的索引。
&j-R1{@[!sm~kIC24558279select * from employee where last_name like '%cliton%';
Xz6nK$i5e6@B24558279在很多情况下可能无法避免这种情况,但是一定要心中有底
f-Z*A5f!_]"s1_-x*f24558279,通配符如此使用会降低查询速度。然而当通配符出现在字ITPUB个人空间 F~An;b FSF
符串其他位置时,优化器就能利用索引。在下面的查询中索ITPUB个人空间N2^r-C#[Ist,H;B K*Y
引得到了使用:ITPUB个人空间OI*L5RYw I#?
select * from employee where last_name like 'c%';
十一、
8A`9ZdM9v-b24558279避免在索引列上使用如下操作符ITPUB个人空间 fp:X9Vb|#o3?
ITPUB个人空间!fj+Fttoc;k:t#g
避免在索引列上使用NOT、OR、IS NULL、IS NOT NULL、!=、||、+等这样的关系操作符或连接符;这样会产生和在索引列上不正确使用函数相同的影响,导致索引失效。ITPUB个人空间(b]t"?9z#d |
0uV v(c v1K*pQ D.K,Uw24558279如果一定要对使用函数的列启用索引, 请使用ORACLE的基于函数的索引。
十二、
y+hqUpz0E24558279避免出现索引列自动转换
UT$z3`t,y'`24558279当比较不同数据类型的数据时, ORACLE自动对列进行简单的类型转换.ITPUB个人空间`!ZP,OCDi
例如:EMP_TYPE是一个字符类型的索引列.
#}:R;QOhTy24558279SELECT USER_NO,USER_NAME,ADDRESSITPUB个人空间0kDQ+}&M)^%Qq/F
FROM USER_FILES
0~ ^!Q0}.](k24558279WHERE USER_NO = 109204421
-U7D%\2RXd_y24558279这个语句被ORACLE转换为:ITPUB个人空间 oO7sj| E%}
SELECT USER_NO,USER_NAME,ADDRESS
*G&b2a!EP7x1J8B24558279FROM USER_FILES
^8o&C'a#B"ok24558279WHERE TO_NUMBER(USER_NO) = 109204421
*F[o3c$QM6US#l#qh24558279因为内部发生的类型转换, 这个索引将失效
十三、
v2`5B3Q.]iJ24558279减少访问数据库的次数ITPUB个人空间)x:E7Ss0D2L
\pz4I?n$w~24558279当执行每条SQL语句时, ORACLE在内部执行了许多工作: 解析SQL语句, 估算索引的利用率, 绑定变量 , 读数据块等等. 由此可见, 减少访问数据库的次数 , 就能实际上减少ORACLE的工作量.
3{9p^cv*NH'C#v24558279 ITPUB个人空间$G%W.O`sB ZD
SQL优化的一个重要目标,减少数据库的逻辑读次数。
十四、ITPUB个人空间.aROL v0T
使用DECODE函数来减少处理时间
FC7B\4XA0X24558279 ITPUB个人空间x'\Z*[FSqiFs
DECODE函数的语法:DECODE(value,if1,then1,if2,then2,if3,then3,...,else),表示如果value等于if1时,DECODE函数的结果返回then1,.value等于if2时,返回then2..,如果value不等于任何一个if值,则返回else值。ITPUB个人空间;]?8K2}7y3eL6{
ITPUB个人空间q'uS2G%FL
使用DECOD函数可以避免重复扫描相同记录或重复连接相同表。
-][0i|I24558279
+U ~ gW:I?W~24558279例如:
-Z Q1Cph5M8~*b n&mO24558279低效:ITPUB个人空间8gxTmpfM)~e]@R
SELECT COUNT(*),SUM(SAL)ITPUB个人空间6tDR)U U*b,_
FROM EMPITPUB个人空间}s+KOW)A'iy
WHERE DEPT_NO = 0020ITPUB个人空间kWVB(CI%s$W1EW
AND ENAME LIKE ‘SMITH%’;
C1q/K x3A24558279SELECT COUNT()SUM(SAL)
十五、
+x.Q4qM:Y!pZ!?%R24558279ORACLE解析器的两个默认解析顺序ITPUB个人空间&D)i%EP/gKUl7bY
X!oDF9TP,Q~!y24558279ORACLE的解析器按照从右到左的顺序处理FROM子句中的表名,因此FROM子句中写在最后的表(基础表)将被最先处理。 在FROM子句中包含多个表的情况下,选择记录条数最少的表作为基础表。(只在基于规则的优化器中有效)。
6T+OBO.JuFQ24558279 ORACLE采用自下而上的顺序解析WHERE子句,那些可以过滤掉最大数量记录的条件必须写在WHERE子句的末尾。ITPUB个人空间f.ro#xI7C
EfzGG@ t4k/sgfI24558279 十六、
gCN-IUDC24558279Order by使用注意事项ITPUB个人空间O[k.Su!x6o.A
ITPUB个人空间Sx+{ cj G V
Order by语句的非索引项或者有计算表达式都将降低查询速度。ITPUB个人空间Kob+V*Zj&q1|B1rgm
ITPUB个人空间i8piJ'LV'_NdHm
Order by语句包含的字段最好创建索引,避免在order by子句中使用表达式(会使索引失效)
十七、
"]x0az(c!y g9l*?24558279优化GROUP BY
{?1N[CvF],oBT24558279 ITPUB个人空间P.}w*\!{_8H
提高GROUP BY 语句的效率, 可以通过将不需要的记录在GROUP BY 之前过滤掉。下面两个查询返回相同结果但第二个明显就快了许多。
FWZ H#Cv,f24558279低效:ITPUB个人空间CT7P!DdC)w_ R
SELECT JOB , AVG(SAL)ITPUB个人空间+_&I_U!oq-D2l{
FROM EMPITPUB个人空间A@wFQGBY
GROUP by JOBITPUB个人空间$q/d9c}9E:X5F
HAVING JOB = 'PRESIDENT'ITPUB个人空间6c:]K#P#nuE
OR JOB = 'MANAGER'
5{s*?C4[N!s24558279高效:
yWxd!ljK24558279SELECT JOB , AVG(SAL)
`TH/@t1J`z1\24558279FROM EMP
'Wg$h-`G}O24558279WHERE JOB = 'PRESIDENT'ITPUB个人空间y7B9vr D^^cJ'vx4E
OR JOB = 'MANAGER' GROUP by JOB
十八、
x#Q2u){y thc24558279用Where子句替换HAVING子句
ld0kj;T24558279避免使用HAVING子句, HAVING 只会在检索出所有记录之后才对结果集进行过滤. 这个处理需要排序ITPUB个人空间r#F;ck%S liL
总计等操作. 如果能通过WHERE子句限制记录的数目,那就能减少这方面的开销.
s2P'ZDGI2JL g$r24558279例如:ITPUB个人空间O-U4~5s5l:N&A~
低效:
dI0kR1y"D24558279SELECT REGION,AVG(LOG_SIZE)
8^Sx{g.G:q_24558279FROM LOCATION
0`d|:X8nI?x24558279GROUP BY REGION
+QZnsH1^Rv24558279HAVING REGION REGION != ‘SYDNEY’
O#h(_"sb_S24558279AND REGION != ‘PERTH’ITPUB个人空间eHP8JtOmp
高效:ITPUB个人空间uzLfgNzk
SELECT REGION,AVG(LOG_SIZE)ITPUB个人空间W`(B?d{
FROM LOCATION
(S6pdxT:F24558279WHERE REGION REGION != ‘SYDNEY’
8~rx:w/T"qa24558279AND REGION != ‘PERTH’
XB%u/vz9T24558279十九、
G;N1L"x#o$Q4|_Z#L24558279几个使用替代的优化写法
xPqqT[F2G4|'}24558279 ITPUB个人空间.k#V dc]?2W
用EXISTS替代IN
)e_B,fBy24558279
+y4d[I5?0[24558279使用NOT EXISTS替代NOT INITPUB个人空间emLv,vPf
ITPUB个人空间"pvk:?2?9]
用IN来替换ORITPUB个人空间1m.E;qM/K3\/A"m
:tFf"eZs24558279用UNION替换OR (适用于索引列)ITPUB个人空间tl&S(}k h7B
ITPUB个人空间R.OG%PF
用>=替代>
f8jOg&[k24558279
Tar;`3p{24558279用< or >替代<>ITPUB个人空间s.l%G-xN(d
7oP,R*gvS6y6]I/NB24558279NOT IN都是最低效的 (因为它对子查询中的表执行了一个全表遍历)
二十、
s QDh b hH24558279能使用外连接,就不要使用子查询
d.kt2M|E%VI24558279inner join 内连接和where相同ITPUB个人空间`+I-ih0zd#MRJ+{1s;D bT
left join 左向外连接,返回左边表所有符合条件的ITPUB个人空间,I;z.D~ ~GA
例如:select * from a, b where a.id=b.id(+);ITPUB个人空间3H9g&@3~j5pB3qk3I$R5a
right join 右向外连接,返回右边表所有符合条件的
cYa*XL0sG24558279例如: select * from a, b where a.id(+)=b.id;ITPUB个人空间%A[ d\4w
full join完整外部连接,左向外连接和右向外连接的合集ITPUB个人空间t,d6q)rG){r1K
交叉连接,也称笛卡儿积,返回左表中的每一行与右表中所有行的组合cross join
二十一、
7JV-gP+WJwh*S-B24558279尽量多使用COMMIT
-^6{gn%x24558279 ITPUB个人空间!X[!haEy
COMMIT所释放的资源:
0W*|e#?(U{%L24558279回滚段上用于恢复数据的信息ITPUB个人空间&X8[ ~(K6Y#ult
被程序语句获得的锁ITPUB个人空间8Gr U#_A0^
redo log buffer 中的空间
EYEcb24558279ORACLE为管理上述3种资源中的内部花费ITPUB个人空间oB2ooA(y)l
ITPUB个人空间Fl3`hb ME8d$l
实际应用中需要注意:ITPUB个人空间*T#t!eOC
循环批量插入大量数据,合理的做法是:设置一个COMMIT频度,比如插入
"z"[`] d/k245582791000条记录后COMMIT一次,这样不会因为频繁COMMIT浪费数据库资源;也不会最后一次COMMIT,如果插入失败,全部回滚。
二十二、ITPUB个人空间&x9K(O6]Wx!J&Q
用TRUNCATE替代DELETE
|(|/Vk4xy6M!h$bE24558279
FA3v5@M [-i"W?24558279如果是全表删除,用TRUNCATE替代DELETE,执行时间大大提高,并且降低高水位线,释放不用的数据块空间,以便数据库重复利用数据块空间。
二十三、ITPUB个人空间FI9\r9e fL9D
使用rowid来写去重sql是效率最高的ITPUB个人空间2r#Aj1hP0s}
表数据去重sql写法有很多,使用rowid来写sql是效率最高的,rowid是一个伪
8~'_~s f@-Pc6d24558279列,通过ROWID可以直接定位到存放相应记录的数据块,然后将其读到内存
C%U*C2j[24558279,以最快的速度找到要去重的记录。
U5lv@!},AY24558279例如:
AKNO!a"{o24558279SQL>DELETE FROM CONFIG_ACTIVE_CODE AITPUB个人空间7A'c.p$M'R,luv n
WHERE ROWID > (
~AU-~&[6`2fK24558279SELECT min(rowid) FROM CONFIG_ACTIVE_CODE B
5bN,H;~7p24558279WHERE A.ACTIVE_CODE= B.ACTIVE_CODE);
二十四、
J+W CBqh#xE!e24558279合理使用表的别名(Alias)ITPUB个人空间*wp8^}*p q]T)P
ITPUB个人空间&K5Ll&F!G
当在SQL语句中连接多个表时, 请使用表的别名并把别名前缀于每个Column上。这样一来,就能够减少解析的时间并减少那些由Column歧义引起的语法错误。
二十五、
9n7V)]3m-a-@.]h[4E24558279合理使用索引ITPUB个人空间&m(_^/v]Y/P] E
D nM"{&D"{"f.g;i24558279检索数据记录条数在表数据总量30%以上,使用索引将不会有显著的效率提高。检索数据记录条数在表数据总量10%以内,通过索引会大大提高数据查询速度。ITPUB个人空间+G}l dk N
索引通常会使SELECT查询变快,但会使INSERT,UPDATE这样的DML变慢(因为要写索引数据,有I/O);要合理使用索引。
Sp {p"PS i4T*`24558279 ITPUB个人空间:sXnn2NB:sR
二十六、ITPUB个人空间3R%aJUF7`gdIf
养成查看执行计划的好习惯ITPUB个人空间v;@&K0Z}9BTwm(dB.l\
为了执行语句,Oracle必须实现许多步骤,这些步骤中的每一步可能是从数
]U/PKCGlvl+Y24558279据库中物理检索数据行,或者用某种方法准备数据行,供发出语句的用户使ITPUB个人空间z&@t C2o1p^:`+@ }
用。Oracle用来执行语句的这些步骤的组合被称之为执行计划。只有知道了ITPUB个人空间rCz S u'y/\
ORACLE在内部到底是如何执行该SQL语句后,才能知道优化器选择的执行ITPUB个人空间iil{OJi(bm
计划是否为最优的。ITPUB个人空间]LS4]iLR
查看执行计划命令: set autot trace exp;ITPUB个人空间7L.~Be'o3L(dg#GP4u
开发人员可以使用TOAD, PL/SQL developer工具查看SQL自己计划,调ITPUB个人空间)d8P/D9L#W"IL4I_
优自己写的SQL语句。
ITPUB个人空间u7xkT*IT
二十七、
Cf3G"L S5s24558279合理使用Hint提示,稳定执行计划ITPUB个人空间o i7eo}S3^J
$@1z8g+M&Ba24558279SQL语句中可以通过提示的方式,指定想要数据库走哪种执行计划,称为hint
例如:
W7@ o]0M3h_:H)q24558279表明对表选择全局扫描的方法.
*Z2Qz J3^ }k1t24558279SELECT /*+FULL(A)*/ EMP_NO,EMP_NAM FROM BSEMPMS A WHERE EMP_NO='CCBZZP';
;]!S%q;k,N&p6D6Z _24558279表明对表选择索引升序的扫描方法.ITPUB个人空间&P \9N@LKH5Y)\
SELECT /*+INDEX_ASC(BSEMPMS PK_BSEMPMS) */ FROM BSEMPMS WHERE DPT_NO='CCBZZP';