sql 優化參考

it_newbalance發表於2013-02-03
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';

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/24558279/viewspace-753843/,如需轉載,請註明出處,否則將追究法律責任。

相關文章