108
" !"#"$%#$ '() *+,-.,/"#0+ 1.23.3 '4/5.3%6/ 7899 :,"%#%#$ ;"4 #$%&%'(%) *+ ,-$%' ./$(/' !"#$%&#&'(#)(%*+'",-&.('

Managing SQL Performance

Embed Size (px)

DESCRIPTION

 

Citation preview

Page 1: Managing SQL Performance

! ! ! ! !! !

! ! ! ! "!

!"#"$%#$&

'()&

*+,-.,/"#0+&1.23.3&'4/5.3%6/&7899&

:,"%#%#$&;"4&

&

&

&

#$%&%'(%)!*+!

,-$%'!./$(/'!

!"#$%&#&'(#)(%*+'",-&.('!

Page 2: Managing SQL Performance

! ! !

!

! ! ! ! ! ! ! ! ! ! !0!

!

Page 3: Managing SQL Performance

! ! ! ! !! !

! ! ! ! 1!

!"#"$%#$&'()&*+,-.,/"#0+&

2/(&/&!3+45/&674!08""!9!:$-6'6';!<-+!08""=81="8!

!

,-$%'!./$(/'[email protected]

http://karenmorton.blogspot.com /

!"#$%&'$()$*"&+$*,%&-&-.$/%0$&+$*($'%1#$'%-%.&-.$"(2$345$6#,)(,'+$%-$#-.,%&-#/7%8*('%*&9$6%,*$()$0(8,$%66:&9%*&(-$:&)#909:#;$<%-%.&-.$"(2$0(8,$345$6#,)(,'+$+"(8:/-=*$>#$%$?(>$%++&.-#/$*($ (-#$ 6#,+(-$ (,$ ,(:#@$ >8*$ *"#$ ?(>$ ()$ #A#,0(-#$ &-A(:A#/$ &-$ *"#$ /#A#:(6'#-*$ %-/$ (-B.(&-.$'%&-*#-%-9#$ %-/$ +866(,*$ ()$ 0(8,$ %66:&9%*&(-+;$ !((:+$ %-/$ *#9"-&C8#+$ :#%,-#/$ 2&::$ "#:6$ 0(8$2,&*#$>#**#,$345$)%+*#,$%-/$#-%>:#$0(8$*($C8&91:0$/&%.-(+#$6((,:0$6#,)(,'&-.$345;$D(8=::$%:+($:#%,-$"(2$ *($ ,#9(.-&E#$%-/$ 9(,,#9*$'%-0$ 9(''(-$%-*&6%**#,-+$ *"%*$'%0$>#$6,#+#-*$ &-$ 0(8,$98,,#-*$9(/#$*"%*$%,#$6,(>:#'+$2%&*&-.$*($"%66#-;$

9 <=:>?;@A:<?=&

:>6&!&%?(6/'!6&!%@?%$5(%)!A6(>!5%$46&&6/'!B$/4!C>-5(%$!D!/B!E@5%$(!F$-?G%!#$-?(6?%&H!F$-?G%!<-(-*-&%!I)46'6&($-(6/'!B$/4!(>%!F-J!:-*G%K!I5$%&&K!08"8L!

M>-(!6&!(>%!B6$&(!(>6';!+/7!(>6'J!/B!A>%'!+/7!&%%!(>%!(/56?!N.-'-;6';!3OP!#%$B/$4-'?%QR!</!+/7!(>6'J!/B!$%&5/'&%!(64%R!</!+/7!(>6'J!/B!7&%$!?/45G-6'(&!-*/7(!(>%!-55G6?-(6/'!$7''6';!N(//!&G/AQR!</!+/7!(>6'J!/B!IMS!/$!I<<.!$%5/$(&R!!

I&!B-$!-&!7&%$&!-$%!?/'?%$'%)K!5%$B/$4-'?%!is $%&5/'&%!(64%L!T&%$&!)/'U(!?-$%!-*/7(!&%$V%$!-')!)-(-*-&%!?/'B6;7$-(6/'&K!'%(A/$J!*-')A6)(>K!W=F!$-(%&K!/$!X7%$+!%@%?7(6/'!5G-'&L!:>%+!?-$%!-*/7(!how fast (>%+!5%$?%6V%!(>%6$!-55G6?-(6/'&!$7'L!IGG!(>-(!/(>%$!&(7BB!6&!;%%J!&5%-J!-')!)/%&'U(!%V%'!*G65!/'!(>%6$!$-)-$L!S%;-$)G%&&!/B!A>%(>%$!/$!'/(!-GG!+/7$!4/'6(/$6';!;-);%(&!BG-&>!;$%%'!G6;>(&!/B!5%$B%?(6/'K!6B!+/7$!7&%$&!-$%!?/45G-6'6';K!+/7UV%!;/(!-!5$/*G%4L!:>%!($7(>!6&K!(>%!&%%)&!/B!(>/&%!5$/*G%4&!V%$+!G6J%G+!A%$%!5G-'(%)!A>%'!(>%!?/)%!A-&!B6$&(!A$6((%'L!

9B9 CD.52%#$&"&*+,-.,/"#0+&!%#D3+2&

.-'-;6';!(>%!5%$B/$4-'?%!/B!+/7$!-55G6?-(6/'!3OP!)/%&'U(!&(-$(!A>%'!+/7$!7&%$&!*%;6'!(/!?/45G-6'L!W(!&(-$(&!*%B/$%!(>%!B6$&(!&(-(%4%'(!6&!%V%$!A$6((%'L!W(!&(-$(&!A>%'!(>%!*7&6'%&&!(-&J&!(>-(!+/7$!-55G6?-(6/'!A6GG!'%%)!(/!&%$V6?%!-$%!)%B6'%)L!F'!-!(64%!G6'%K!(>-(!&(-$(6';!5/6'(!-')!(>%!B6$&(!7&%$!?/45G-6'(!-*/7(!5%$B/$4-'?%!?/7G)!*%!X76(%!B-$!-5-$(L!Y7(!W!-*&/G7(%G+!*%G6%V%!(>-(!+/7!>-V%!(/!&(-$(!*+!?/'&6)%$6';!+/7$!7&%$U&!%@5%$6%'?%L!

! WB!+/7!&(-$(!*+!(>6'J6';!/B!>/A!+/7$!7&%$!A6GG!%@5%$6%'?%!+/7$!-55G6?-(6/'K!(>6&!645G6%&!(>-(!4-'-;6';!3OP!5%$B/$4-'?%!6&!B6$&(!-*/7(!-!46')&%(K!'/(!-!)-(-&%(L!Z/7$!46')&%(!6&K!6'!5-$(K!$%G-(%)!(/!(>%!&%(!/B!$7G%&!+/7UV%!6'(%$'-G6[%)L!Y7(!6(U&!-G&/!-*/7(!+/7$!*%G6%B&!-')!B%%G6';&!$%G-(%)!(/!A>-(!5%$B/$4-'?%!6&!-')!4%-'&L!</!+/7!(>6'J!4-'-;6';!5%$B/$4-'?%!6&!>-$)R!</!+/7!(>6'J!4-'-;6';!5%$B/$4-'?%!6&K!/$!6&'U(K!+/7$!$%&5/'&6*6G6(+R!</!+/7!(>6'J!5%$B/$4-'?%!6&!&/4%(>6';!(/!(>6'J!-*/7(!G-(%$K!A>%'K!/$!6BK!5$/*G%4&!-$6&%R!</!+/7!(>6'J!4-'-;6';!5%$B/$4-'?%!6&!-*/7(!-V/6)6';!?-(-&($/5>%&!/$!-*/7(!%'V6&6/'6';!5/&&6*6G6(6%&R!

Page 4: Managing SQL Performance

! ! !

!

! ! ! ! ! ! ! ! ! ! !\!

3/4%(64%&!+/7$!46')&%(!6&!6'BG7%'?%)!*+!+/7$!]/*!)%&?$65(6/'L!WB!+/7!>-V%!'/(!&5%?6B6?-GG+!*%%'!(-&J%)!A6(>!5%$B/$4-'?%!-&!-!?/'?%$'K!+/7!4-+!G6J%G+!6;'/$%K!/$!-(!(>%!V%$+!G%-&(!46'646[%K!+/7$!$/G%!6'!%'&7$6';!/5(64-G!5%$B/$4-'?%!/B!(>%!3OP!+/7!A$6(%L!Y7(!$%;-$)G%&&!/B!+/7$!)%B6'%)!]/*!$/G%K!W!)/!*%G6%V%!(>-(!%BB%?(6V%!3OP!5%$B/$4-'?%!4-'-;%4%'(!&(-$(&!A6(>!+/7$!46')&%(L!W(U&!>/A!+/7!V6%A!+/7$!$/G%!-')!(>%!?/'($6*7(6/'&!+/7!4-J%!6'!$%;-$)!(/!(>%!5%$B/$4-'?%!/B!+/7$!?/)%!(>-(!4-J%&!(>%!)6BB%$%'?%!*%(A%%'!-'!/5(64-GG+!5%$B/$46';!-55G6?-(6/'!-')!-!5//$G+!5%$B/$46';!/'%L!

C/'&6)%$!(>%!)%B6'6(6/'!/B!(>%!A/$)!manageH!

"L (/!>-')G%!/$!)6$%?(!A6(>!-!)%;$%%!/B!&J6GGH!-&!!

a: (/!4-J%!-')!J%%5!?/45G6-'(!!

b: (/!($%-(!A6(>!?-$%H!>7&*-')!!

c: (/!%@%$?6&%!%@%?7(6V%K!-)46'6&($-(6V%K!-')!&75%$V6&/$+!)6$%?(6/'!/B!

0L (/!A/$J!75/'!/$!($+!(/!-G(%$!B/$!-!57$5/&%!

1L (/!&7??%%)!6'!-??/45G6&>6';H!?/'($6V%!

—.%$$6-4^M%*&(%$!F'G6'%!www.merriam-webster.com/dictionary/manage

:>6&!)%B6'6(6/'!6')6?-(%&!(>-(!6B!+/7!A-'(!&/4%(>6';!(/!*%!4-'-;%-*G%K!+/7!47&(!;6V%!6(!&J6GG%)!

-((%'(6/'!-')!%BB/$(L!3/K!B6$&(!-')!B/$%4/&(K!W!(>6'J!4-'-;6';!5%$B/$4-'?%!6&!-*/7(!6'(%;$-(6';!/'%!&645G%!5$6'?65G%!6'(/!+/7$!46')&%(H!I am responsible for the performance of the code I write or maintainL!M6(>/7(!-!?/'&?6/7&!5%$&/'-G!?>/6?%!(/!-??%5(!$%&5/'&6*6G6(+!B/$!6(K!5%$B/$4-'?%!A6GG!not *%!4-'-;%-*G%L!

! :/!4-'-;%!5%$B/$4-'?%K!+/7!B6$&(!'%%)!(/!J'/A!>/A!-')!A>+!F$-?G%!)%(%$46'%&!(>%!5G-'!/5%$-(6/'&!B/$!%-?>!X7%$+L!:>%'!+/7!'%%)!(/!*%!-*G%!(/!%-&6G+!-')!-??7$-(%G+!?-5(7$%!)6-;'/&(6?&!6')6?-(6';!A>-(!+/7$!-55G6?-(6/'!?/)%!6&!)/6';!-&!6(!%@%?7(%&L!:>-(!4%-'&!G%-$'6';!>/A!F$-?G%U&!?/&(^*-&%)!/5(646[%$!A/$J&K!5-$(6?7G-$G+!>/A!6(!7(6G6[%&!&(-(6&(6?&L!I')!6(!4%-'&!7')%$&(-')6';!(>%!645/$(-'?%!/B!>-V6';!+/7$!-55G6?-(6/'!A%GG!6'&($74%'(%)L!

! :>%!&(-(6&(6?&!7&%)!*+!(>%!/5(646[%$!-$%!G6J%!B7%G!B/$!+/7$!?-$L!:>%!X7-G6(+!/B!(>%!B7%G!+/7!57(!6'(/!+/7$!V%>6?G%!-BB%?(&!>/A!A%GG!+/7$!?-$!>-')G%&K!>/A!47?>!;-&!46G%-;%!6(!;%(&K!>/A!/B(%'!6(!'%%)&!4-6'(%'-'?%K!-')!%V%'!>/A!G/';!+/7$!V%>6?G%!A6GG!*%!&%$V6?%-*G%L!T')%$&(-')6';!A>-(!;/%&!6'!(/!(>%!/5(646[%$!&/!(>-(!6(!?-'!?>//&%!A>6?>!3OP!%@%?7(6/'!5G-'!/5%$-(6/'&!-$%!*%&(!>%G5&!+/7!J'/A!A>-(!&>/7G)!$%-&/'-*G+!*%!%@5%?(%)!(/!/??7$L!I')!6B!+/7!)/'U(!;%(!(>%!$%&7G(&!+/7!%@5%?(!/$!;%(!(>%!5%$B/$4-'?%!+/7!'%%)K!+/7!?-'!-)]7&(!(>%!B7%GL!

! IB(%$!+/7!7')%$&(-')!A>-(!;/%&!6'!(/!(>%!/5(646[%$!&/!6(!?-'!4-J%!(>%!*%&(!5G-'!?>/6?%&K!+/7!(>%'!'%%)!(/!*%!-*G%!(/!?-5(7$%!)6-;'/&(6?&!X76?JG+!-')!-??7$-(%G+L!:>%$%!-$%!4-'+!A-+&!(/!?-5(7$%!)6-;'/&(6?!)-(-K!*7(!4-'-;6';!5%$B/$4-'?%!A%GG!$%X76$%&!(>-(!+/7!*%!-*G%!(/!%-&6G+!?/GG%?(!(>%!4%($6?&!+/7!'%%)K!A>%'!+/7!'%%)!(>%4L!:>%!*%&(!A-+!(/!)/!(>6&!6&!(/!5$/5%$G+!6'&($74%'(!+/7$!?/)%L!Instrumentation 6&!]7&(!-!B%A!%@($-!G6'%&!/B!?/)%!+/7!-))!(/!+/7$!-55G6?-(6/'!(/!%'-*G%!+/7!(/!6)%'(6B+!(>%!(-&J&!6(!%@%?7(%&!_(>-(!6&K!3OP!$%G-(%)!(/!*7&6'%&&!(-&J&`!&/!(>%+!-$%!%-&+!(/!B6')!-')!4/'6(/$L!

2/5%B7GG+K!WUV%!%&(-*G6&>%)!&/!B-$!(>-(!4-'-;6';!3OP!5%$B/$4-'?%!&(-$(&!A6(>!-'!-((6(7)%K!-!46')&%(L!Z/7!-??%5(!$%&5/'&6*6G6(+!B/$!(>%!5%$B/$4-'?%!/B!%V%$+!&(-(%4%'(!+/7!A$6(%!/$!4-6'(-6'L!Z/7!*76G)!B/7')-(6/'!J'/AG%);%!-*/7(!>/A!(>%!/5(646[%$!A/$J&!-')!7&%!(>-(!J'/AG%);%!(/!B%%)!(>%!/5(646[%$!A6(>!X7-G6(+!&(-(6&(6?&!-')!X7-G6(+!?/)%L!Z/7!4-J%!+/7$!-55G6?-(6/'!%-&+!(/!4/'6(/$!*+!-))6';!6'&($74%'(-(6/'!(>-(!A6GG!>%G5!

Page 5: Managing SQL Performance

! ! ! ! !! !

! ! ! ! a!

+/7!;%(!(>%!$6;>(!5%$B/$4-'?%!4%($6?&!A>%'!+/7!'%%)!(>%4L!:>%!*/((/4!G6'%!6&!(>-(!+/7$!46')&%(!6&!;%-$%)!(/A-$)!4-'-;6';!5%$B/$4-'?%!%V%$+!)-+!-')!'/(!]7&(!A>%'!5$/*G%4&!-$6&%L!

:>%!%'(6$%!?>-5(%$!>-&!*%%'!$%5$6'(%)!-')!6'?G7)%)!-(!(>%!%')!/B!(>6&!)/?74%'(!B/$!+/7$!$%B%$%'?%L!

7 !C=CE<=E&'()&*F>G?>!C=AF&

.-'-;6';!3OP!5%$B/$4-'?%!6&!B6$&(!-*/7(!-!46')&%(K!'/(!-!)-(-&%(L!

!

!

!

!

S%5%-(!-B(%$!4%H!!

! W!-4!$%&5/'&6*G%!B/$!(>%!5%$B/$4-'?%!/B!(>%!?/)%!W!A$6(%!/$!4-6'(-6'L!

7B9 :H+&E,+"2&;%I%D+&

:>%$%!6&!-!)6V6)%!(>-(!6&!/B(%'!B-?6G6(-(%)K!J'/A6';G+!/$!7'J'/A6';G+K!*+!4/&(!?/45-'6%&L!:>6&!6&!(>%!)6V6)%!*%(A%%'!(>%!<-(-*-&%!I)46'6&($-(/$&!-')!(>%!<%V%G/5%$&L!M>6G%!(>%!(A/!;$/75&!%')!;/-G!4-+!*%!(/!5$/V6)%!(>%6$!?7&(/4%$&!A6(>!-!&(-*G%!-')!%BB6?6%'(!-55G6?-(6/'K!(>%6$!5%$&5%?(6V%&!-')!5%$?%6V%)!'%%)&!(/!-?>6%V6';!(>%!%')!;/-G!6&!/B(%'!$-)6?-GG+!)6BB%$%'(L!:>6&!)6V6)%!?-'!?-7&%!B$6?(6/'!*%(A%%'!(>%!(A/!;$/75&!-')!-G&/!4-J%!6(!%-&+!B/$!%-?>!;$/75!(/!5/6'(!(/!(>%!/(>%$!A>%'!5%$B/$4-'?%!6&&7%&!-$6&%L!

Page 6: Managing SQL Performance

! ! !

!

! ! ! ! ! ! ! ! ! ! !D!

!

!

<%V%G/5%$&!4-+!(-J%!(>%!-((6(7)%!(>-(!(>%6$!]/*!6&!(/!5$/)7?%!-55G6?-(6/'&!$6?>!A6(>!B%-(7$%&!7&%$&!A-'(!-')!)%4-')L!:>%!)-(-*-&%!6&!&645G+!-!*G-?J!*/@!(>%+!47&(!6'(%$-?(!A6(>!6'!/$)%$!(/!;%(!(>%!)-(-L!2/A!(>%+!;/!-*/7(!;%((6';!(>-(!)-(-K!-')!(>%!5%$B/$4-'?%!645G6?-(6/'&!/B!(>%6$!4%(>/)&K!4-+!'/(!%V%'!*%!5-$(!/B!(>%!%X7-(6/'!-&!(>%+!$7&>!(/!5$/V6)%!$-56)!)%5G/+4%'(!/B!B7'?(6/'&!-')!B%-(7$%&L!

<YI&!/B(%'!A6GG!/B(%'!B-?6G6(-(%!(>%!)%V%G/5%$U&!*G-?J!*/@!4%'(-G6(+!*+!G646(6';!-??%&&!(/!/*]%?(&!-')!)-(-!'%%)%)!(/!5$/5%$G+!$%V6%AK!(%&(!-')!V%$6B+!3OP!5%$B/$4-'?%L!:>6&!4-+!?/4%!B$/4!(>%!B%-$!/B!G/&6';!?/'($/G!/B!(>%!%'V6$/'4%'(!*+!/5%'6';!75!-??%&&!(/!%G%4%'(&!(>%+!?/'&6)%$!(/!*%!*%+/')!(>%!7')%$&(-')6';!/B!'/'^<YI&L!:>%!<YI!4-+!*%!-*G%!(/!B6')!N*-)Q!3OP!-')!J6?J!6(!*-?J!(/!(>%!)%V%G/54%'(!(%-4K!*7(!(>%!)%V%G/5%$&!-$%!>-4&($7';!(/!)/!-'+(>6';!%@?%5(!;7%&&!-(!A>-(!645$/V%4%'(&!4-+!>%G5!&6'?%!(>%+!)/'U(!>-V%!-??%&&!(/!A>-(!(>%+!'%%)L!

7B7 E%I+&J+/&KH"2&2H+4&#++DL&

S%;-$)G%&&!/B!(>%6$!$/G%K!;6V6';!%-?>!5%$&/'K!A>-(!(>%+!'%%)!(/!4-J%!5%$B/$4-'?%!-'-G+&6&!-!5-$(!/B!(>%6$!)-+^(/^)-+!-?(6V6(6%&!6&!?$6(6?-G!(/!4-'-;6';!3OP!5%$B/$4-'?%!?/'&6&(%'(G+L!M>-(!6&!'%%)%)!6&!$%-GG+!X76(%!&645G%L!b7&(!*%?-7&%!(>%!'%%)&!-$%!&645G%!)/%&'U(!4%-'!(>%+!-$%!4%(L!

7B7B9 :,"%#%#$&

:$-6'6';!6&!/'%!/B!(>%!4/&(!645/$(-'(!'%%)&!(>-(!47&(!*%!4%(L!:>%$%!-$%!(A/!4-6'!>6')$-'?%&!(/!;%((6';!%V%$+/'%!(>%!($-6'6';!(>%+!'%%)H!(64%!-')!4/'%+L!:>%!'/$4-G!)-+^(/^)-+!)%4-')&!/B!?/45G%(6';!$%X76$%)!(-&J&!?-'!G%-V%!G6((G%!(64%!B/$!($-6'6';L!:-J6';!(64%!-A-+!B$/4!(>%!/BB6?%!B/$!($-6'6';!$%X76$%&!(>-(!)-+^(/^)-+!(-&J&!-')!$%&5/'&6*6G6(6%&!%6(>%$!B-GG!/'!&/4%/'%!%G&%K!/$!;%(!5/&(5/'%)!7'(6G!(>%!5%$&/'!$%(7$'&!(/!(>%!/BB6?%L!I')K!A6(>!*7);%(&!*%6';!(6;>(G+!4-'-;%)K!4/'6%&!B/$!($-6'6';!&%%4!(/!*%!/'%!/B!(>%!B6$&(!(>6';&!(/!;%(!?7(!A>%'!?/&(^&-V6';&!-$%!'%%)%)L!

! 3/K!>/A!)/!+/7!-?X76$%!(>%!&J6GG&!'%%)%)!B/$!+/7!-')!+/7$!(%-4&!6B!*/(>!(64%!-')!4/'%+!-$%!B-?(/$&R!M%GGK!WUGG!B-GG!*-?J!/'!(>%!/G)!-)-;%!NA>%$%!(>%$%U&!-!A6GG!(>%$%U&!-!A-+LQ!:A/!/5(6/'&!?/4%!644%)6-(%G+!(/!46')!B/$!($-6'6';!(>-(!?-'!*%!6'%@5%'&6V%!6'!(%$4&!/B!*/(>!(64%!-')!4/'%+!&5%'(L!

• Y$/A'!*-;!G7'?>%&!

• P6V%='%(!4%%(6';!

Page 7: Managing SQL Performance

! ! ! ! !! !

! ! ! ! c!

:-J6';!-'!>/7$!/V%$!G7'?>!(/!?/')7?(!*$/A'!*-;!&%&&6/'&!?-'!5$/V6)%!-!A-+!B/$!?/^A/$J%$&!(/!*76G)!(>%6$!J'/AG%);%!-')!&J6GG!G%V%G&!A6(>/7(!(-J6';!G-$;%!?>7'J&!/B!(64%!-A-+!B$/4!(>%!'/$4-G!$/7(6'%L!:>%&%!&%&&6/'&!?-'!*%!?/')7?(%)!*+!(%-4!4%4*%$&!A>/!$/(-(%!(-J6';!/'!(>%!$/G%!/B!($-6'%$L!E-?>!5%$&/'!?-'!&(-$(!*+!?/')7?(6';!/'%!/$!4/$%!&%&&6/'&!?/V%$6';!&J6GG&!B/$!A>6?>!(>%+!-$%!-G$%-)+!5$/B6?6%'(L!F(>%$!&J6GG&!(>-(!'%%)!(/!*%!-))%)!(/!(>%!;$/75!?-'!*%!&5G6(!75!-4/';!(>%!(%-4!4%4*%$&!-')!%-?>!5%$&/'!?-'!$%&%-$?>K!&(7)+!-')!5$%5-$%!4-(%$6-G&!(/!(>%'!&>-$%!A6(>!(>%!;$/75L!

I'/(>%$!*$/A'!*-;!/5(6/'!?/7G)!*%!(/!G/?-(%!($-6'6';!-V-6G-*G%!B$/4!(>%!6'(%$'%(!-')!&?>%)7G%!-!G7'?>!&%&&6/'!B/$!(>%!(%-4!(/!4%%(!6'!/'%!?/'B%$%'?%!$//4!-')!5$/]%?(!(>%!($-6'6';!B/$!(>%!A>/G%!;$/75L!F$K!4-J%!-$$-';%4%'(&!B/$!-!&%$6%&!/B!&%&&6/'&!A6(>!G/?-G!_/$!$%4/(%`!%@5%$(&!A>/U)!*%!A6GG6';!(/!A/$J!A6(>!+/7!(/!)%V%G/5!&>/$(!&5%?6B6?!&%;4%'(&!B/$!]7&(!(>6&!57$5/&%L!E';-;6';!/7(&6)%!V%')/$&!4-+!4%-'!+/7!&5%')!-!G6((G%!4/'%+K!*7(!(>6'J!/7(&6)%!(>%!*/@!-!*6(!/'!A>-(!6&!5/&&6*G%!-')!+/7!'%V%$!J'/A!A>-(!+/7!4-+!*%!-*G%!(/!A/$J!/7(L!

WB!+/7$!(%-4!6&!'/(!-GG!G/?-(%)!6'!(>%!&-4%!/BB6?%K!+/7!4-+!'%%)!(/!)/!(>%&%!J6')&!/B!&%&&6/'&!7&6';!P6V%!.%%(6';K!d/:/.%%(6';!/$!A>-(%V%$!/(>%$!$%4/(%!4%%(6';!&%$V6?%!+/7!-$%!B-46G6-$!A6(>L!W'!;%'%$-GK!(>%!7&%!/B!(>%&%!(+5%&!/B!&%$V6?%&!(>-(!-GG/A!+/7!(/!-GG/A!/(>%$&!(/!V6%A!+/7$!&?$%%'!4-J%&!6(!47?>!%-&6%$!(/!A/$J!6'!&>/$(!($-6'6';!&%&&6/'&!A6(>!;$%-(%$!%-&%L!

7B7B7 A.//6#%0"2%.#&

I&!A%!>-V%!-G$%-)+!)6&?7&&%)K!(>%$%!6&!/B(%'!-!)6V6)%!*%(A%%'!<YI&!-')!)%V%G/5%$&!?-7&%)K!6'!5-$(K!*+!(>%6$!)6BB%$%'(!5%$&5%?(6V%&L!M>%$%!(>%&%!)6BB%$%'?%&!*%?/4%!-55-$%'(!6&!6'!(>%!A-+!(>%!;$/75&!?/447'6?-(%K!/$!5%$>-5&!6(!A/7G)!*%!*%((%$!&(-(%)!(/!&-+!6'!(>%!A-+!(>%!;$/75&!)/'U(!?/447'6?-(%L!

! 36'?%!(>%!5$64-$+!B/?7&!/B!%-?>!;$/75!6&!/B(%'!X76(%!)6BB%$%'(K!(>%!G-';7-;%!7&%)!-')!(>%!G%V%G!/B!7')%$&(-')6';!%-?>!;$/75!>-&!/B!(>%!/(>%$!?-'!*%?/4%!5-$(!/B!(>%!5$/*G%4!6'!;%((6';!5%$B/$4-'?%!5$/*G%4&!>-')G%)!6'!-!(64%G+!B-&>6/'L!WUV%!&%%'!-!5$/*G%4!;%(!*/7'?%)!*-?J!-')!B/$(>!*%(A%%'!;$/75&!6'!%4-6G!?>-6'&!(>-(!$7'!a8!(>$%-)&!)%%5L!Y+!(>%!(64%K!%V%$+/'%!>-&!A-)%)!(>$/7;>!(>%!%'(6$%!%@?>-';%K!(>%!/$6;6'-G!5$/*G%4!/B(%'!;%(&!G/&(L!

!

! WUV%!/B(%'!A/')%$%)!A>%'!6(!A-&!(>-(!B-?%^(/^B-?%!?/'V%$&-(6/'&!/$!5>/'%!?-GG&!*%?-4%!&/4%(>6';!(/!-V/6)!-&!47?>!-&!+/7!A/7G)!(>%!5G-;7%L!#%$>-5&!6(!>-&!&/4%(>6';!(/!)/!A6(>!%V%$+/'%!A-'(6';!(/!>-V%!?/447'6?-(6/'&!)/?74%'(%)!&/!(>-(!(>%+!?-'!>-V%!-!($-6G!/B!?/'V%$&-(6/'!(>%+!?-'!5/6'(!*-?J!(/!G-(%$L!Y7(K!W!(>6'J!(>%!)6V6)%!*%(A%%'!;$/75&!6&!6'?$%-&%)!*+!G-?J!/B!5%$&/'-GK!G6V%!?/'(-?(L!:>%$%!>-V%!*%%'!(64%&!A>%'!WUV%!]7&(!A-GJ%)!)/A'!(>%!>-GG!(/!&/4%/'%U&!-')!A-&!-*G%!(/!7&%!-!A>6(%*/-$)!-')!(-J%!"8!46'7(%&!(/!(-GJ!

Page 8: Managing SQL Performance

! ! !

!

! ! ! ! ! ! ! ! ! ! !e!

-*/7(!(>%!6&&7%!-')!6(!A-&!>-')G%)L!:>%!-G(%$'-(6V%!6&!4/&(!/B(%'!&5%')6';!>-GB!(>%!)-+!&%')6';!%4-6G!*-?J!-')!B/$(>!A6(>!'/!&/G7(6/'!-')!B$7&($-(6/'!G%V%G&!6'?$%-&6';!-&!(64%!BG6%&!*+L!

! E4-6G!?-'!*%!X76(%!(%$&%!-')!/B(%'!6(!6&!V6$(7-GG+!645/&&6*G%!(/!5$/5%$G+!6']%?(!%4/(6/'!6'!(%@(!B/$4L!:-J%!(>6&!&6(7-(6/'!B/$!%@-45G%H!W!A-&!/'!(>%!)6&($6*7(6/'!/B!-'!%4-6G!(>-(!?/'(-6'%)!-!B%A!5-$-;$-5>&!%@5G-6'6';!&/4%(>6';!-')!-&J%)!/'%!/$!(A/!X7%&(6/'&!(/!A>6?>!(>%!&%')%$!'%%)%)!-!$%&5/'&%L!W!)6)'U(!6'B%$!-'+!(/'%!/$!%4/(6/'!6'!6(!A>%'!W!$%-)!6(!*7(!-'/(>%$!5%$&/'!&%'(!*-?J!-!$%5G+!&-+6';K!N:/7?>+K!(/7?>+L!M>%'!A%!-GG!?-G4!)/A'!4-+*%!A%UGG!;%(!(/!(>%!-'&A%$&LQ!27>R!I!4%%(6';!A-&!?-GG%)!B/$!(>%!'%@(!4/$'6';!(/!)6&?7&&!(>%!6&&7%!-')!(>6';&!A%$%!(-J%'!?-$%!/B!6'!]7&(!-!B%A!46'7(%&!A6(>!'/!/'%!46&6'(%$5$%(6';!A>-(!A-&!*%6';!&-6)L!WB!(>%!4%%(6';!>-)!*%%'!?-GG%)!6'6(6-GG+K!(>%!6&&7%!?/7G)!>-V%!*%%'!>-')G%)!A6(>6'!46'7(%&!6'&(%-)!/B!%@(%')6';!(/!-!&%?/')!)-+L!

! W!J'/A!A%U$%!-GG!*7&+!-')!%4-6G!6&!/B(%'!-!A-+!(/!X76?JG+!$%&5/')K!*7(!WU)!&7;;%&(!(>-(!%4-6G!?/7G)!/B(%'!*%!7&%)!(/!?/'B6$4!-')!B/$4-G6[%!6'^5%$&/'!/$!5>/'%!?/'V%$&-(6/'&!-')!(/!5$/V6)%!(>%!)/?74%'(-(6/'!($-6GL!WUV%!;/((%'!6'(/!(>%!>-*6(!/B!/'G+!?>%?J6';!4+!%4-6G!/'?%!%V%$+!>/7$!/$!(A/!)%5%')6';!/'!A>-(!WU4!A/$J6';!/'!&/!-&!(/!'/(!&5%')!-GG!4+!(64%!(>%$%L!I')K!A>-(!WUV%!-?(7-GG+!*%%'!;G-)!(/!&%%!6&!(>-(!6B!&/4%/'%!&%')&!/7(!-'!%4-6G!(>-(!(>%+!A-'(!4%!(/!-'&A%$!6'!0!46'7(%&K!-')!W!)/'U(K!(>%+UGG!(+56?-GG+!56?J!75!(>%!5>/'%!-')!?-GG!4%L!W!(>6'J!WU4!/'!(/!&/4%(>6';f!

7B7BM C00+33&

2-V6';!(>%!5$6V6G%;%&!(/!-??%&&!(>%!'%?%&&-$+!/*]%?(&!-')!&755G6%)!5-?J-;%&!(>-(!5$/V6)%!5%$B/$4-'?%!)-(-!6&!-!?$6(6?-G!'%%)!(>-(!W!&%%!)%'6%)!(/!)%V%G/5%$&!-;-6'!-')!-;-6'L!M>6G%!W!?%$(-6'G+!7')%$&(-')!(>%!'%%)!(/!G646(!-??%&&!6'!5$/)7?(6/'!-')!J%+!5$%^5$/)7?(6/'!%'V6$/'4%'(&K!6(!6&!V%$+!)6BB6?7G(!B/$!4%!(/!7')%$&(-')!(>%!]7&(6B6?-(6/'!B/$!5$/>6*6(6';!-??%&&!6'!(>%!)%V%G/54%'(!-')!(%&(6';!%'V6$/'4%'(&L!WUV%!>%-$)!-GG!(>%!-$;74%'(&!B/$!A>+!-??%&&!6&!/B(%'!G646(%)K!*7(!6B!A%!?-'!-;$%%!(>-(!5%$B/$4-'?%!6&!%V%$+/'%U&!$%&5/'&6*6G6(+K!V6$(7-GG+!-GG!(>%!-$;74%'(&!(/!5$/>6*6(=G646(!-??%&&!;/!/7(!(>%!)//$L!

I??%&&!(/!(>%!%-?>!/B!(>%!B/GG/A6';!6&!?$6(6?-GH!!

• )+'-46?!5%$B/$4-'?%!_gh`!V6%A&!_G6J%!gh3E33WFiK!gh3OPj#PIiK!%(?L`!

• %@(%')%)!3OP!($-?%!)-(-!_-??%&&!(/!T3ESj<T.#j<E3:`!

• &755G6%)!5-?J-;%&!_G6J%!<Y.3j.FiW:FSK!<Y.3jT:WPW:ZK!%(?L`!

:>%!4/$%!-??%&&!(/!(>%&%!/*]%?(&!6&!G646(%)K!(>%!G%&&!%BB%?(6V%!-!5%$&/'!?-'!*%!A>%'!-((%45(6';!(/!$%V6%A!-')!/5(646[%!5%$B/$4-'?%L!k/$!%@-45G%K!G%(U&!&-+!-!J%+!*7&6'%&&!7&%$!?-GG%)!(>%!>%G5!)%&J!A6(>!-!?/45G-6'(!-*/7(!(>%!5%$B/$4-'?%!/B!(>%!NC7&(/4%$!3%-$?>Q!&?$%%'L!:>%!5$/*G%4!6&!$%5/$(%)!B6$&(!(/!(>%!<YI!_A>/K!6B!+/7!!$%4%4*%$!B$/4!/7$!%-$G6%$!)6&?7&&6/'`!)/%&'U(!J'/A!-'+(>6';!47?>!-*/7(!(>%!-55G6?-(6/'L!:>%!<YI!;/%&!/7(!(/!;$-*!-'!IMS!/$!I32!$%5/$(!B/$!(>%!(64%!5%$6/)!)7$6';!A>6?>!(>%!?/45G-6'(!A-&!B6G%)L!F'!(>-(!$%5/$(K!(>%+!&%%!-!?/75G%!/B!3OP!&(-(%4%'(&!(>-(!&>/A!75!-(!(>%!(/5!/B!(>%!G6&(!B/$!%G-5&%)!(64%L!:>%+!?/5+!(>%!3OP!&(-(%4%'(!-')!&%')!6(!(/!(>%!)%V%G/54%'(!(%-4!A6(>!-!'/(%!&-+6';K!N#G%-&%!4-J%!(>6&!$7'!B-&(%$LQ!:>%!)%V%G/5%$!A>/!;%(&!(>%!3OP!%@%?7(%&!6(!6'!(>%6$!(%&(!%'V6$/'4%'(!-')!&-+&K!NW(!A/$J&!B6'%!A>%'!W!$7'!6(LQ!FB!?/7$&%K!&6'?%!(>%!)%V%G/5%$!)/%&'U(!>-V%!-??%&&!(/!-'+(>6';!(/!*%!-*G%!(/!)/!-!4/$%!(>/$/7;>!-'-G+&6&K!(>-(U&!-*/7(!-GG!(>%+!?-'!)/!B/$!'/AL!:>%!<YI!&>$7;&!-')!(>6'J&!6(!47&(UV%!*%%'!-!N;G6(?>Q!-')!B/$;%(&!-*/7(!6(L!!

I!G6((G%!A>6G%!G-(%$K!(>%!7&%$!?-GG&!6'!-;-6'!A6(>!4/$%!?/45G-6'(&!&/!(>%!<YI!;/%&!-!&(%5!B7$(>%$!-')!;$-*&!(>%!%@%?7(6/'!5G-'!B$/4!IMS!B/$!(>6&!5-$(6?7G-$!3OP!-')!&%')&!(>-(!*-?J!(/!(>%!)%V%G/5%$!&-+6';K!N:>%$%U&!;/(!(/!*%!-!5$/*G%4!-&!(>%!7&%$!6&!&(6GG!?/45G-6'6';L!:-J%!-!G//J!-(!(>6&!5G-'l!6(!G//J&!0"1L!WU4!&7$%!+/7UGG!*%!-*G%!(/!B6@!6(!/'?%!+/7!(-J%!-!G//J!-(!(>6&LQ!:>%!)%V%G/5%$!G//J&!-(!(>%!5G-'!-')!)/%&'U(!&%%!-'+!B7GG!(-*G%!&?-'&!-')!(>%!

Page 9: Managing SQL Performance

! ! ! ! !! !

! ! ! ! m!

?/&(!&>/A'!6&!/'G+!"8L!3?$-(?>6';!>6&!>%-)K!>%!?-GG&!(>%!<YI!-')!&-+&K!NW(U&!]7&(!'/(!-!5$/*G%4!A6(>!(>%!3OPL!:>-(!5G-'!G//J&!+((1!(/!4%L!W(!47&(!*%!-!)-(-*-&%!5$/*G%4LQ!:>%!<YIK!A>/!-*&/G7(%G+!!%(23!>6&!)-(-*-&%!6&!]7&(!B6'%!;$74*G%&!-*/7(!(>%!)%V%G/5%$&!-')!&(-$(&!($+6';!(/!B6;7$%!/7(!(>%!5$/*G%4!>64&%GBL!

W!?/7G)!;/!/'!G6J%!(>6&!-)!'-7&%74K!*7(!WUGG!&5-$%!+/7!(>%!(%)674L!:>%!B-?(!6&!(>-(!6'!&/4%!&>/5&!(>6&!/??7$&!X76(%!B$%X7%'(G+L!C/45G-6'(&!-*/7(!5//$G+!5%$B/$46';!3OP!(7$'!6'(/!-!56';^5/';!4-(?>!*%(A%%'!(>%!<YI&!-')!)%V%G/5%$&!A6(>!'/!/'%!4-J6';!-'+!5$/;$%&&L!.7?>!/B!(>6&!A-&(%)!%BB/$(!?/7G)!*%!-V/6)%)!6B!%V%$+/'%!>-)!-??%&&!(/!A>-(!(>%+!'%%)!-')!?/7G)!A/$J!(>%!5$/*G%4!-&!-!(%-4!6'&(%-)!/B!*/7'?6';!(>%!5$/*G%4!*-?J!-')!B/$(>!A6(>!'/!$%-G!5$/;$%&&L!

W'!(>%!%')K!(>%!J%+!6&!(/!4-J%!6(!-&!&645G%!-')!%-&+!-&!5/&&6*G%!(/!;%(!(>%!)-(-!'%%)%)!(/!X76?JG+!-'-G+[%!-')!?/$$%?(!-!3OP!5%$B/$4-'?%!5$/*G%4L!F'%!A-+!(/!4-J%!6(!%-&+!6&!(/!-$4!%V%$+/'%!A6(>!(>%!(//G&!(>%+!'%%)!(/!;%(!(>%!6'B/$4-(6/'!(>%+!'%%)L!:>%!(//G&!?-'!*%!&?$65(&!/$!-!G6*$-$+!/B!?/44/'G+!7&%)!X7%$6%&!/$!-!57$?>-&%)!5$/)7?(L!W!>-V%!-!&%(!/B!&?$65(&!-')!X7%$6%&!(>-(!W!7&%!(/!>%G5!?/GG%?(!%V%$+(>6';!W!(+56?-GG+!'%%)L!W!-G&/!>-V%!-!B%A!;$%-(!(//G&!G6J%!(>%!2/(&/&!#$/B6G%$!_-')!$%G-(%)!3OP!<%V%G/5%$!%@(%'&6/'`L!.%(>/)!S!-G&/!>-&!-!;$%-(!&%(!/B!(//G&K!'-4%)!.S://G&!-')!.S:$-?%K!&5%?6B6?-GG+!?$%-(%)!(/!>%G5!+/7!A/$J!A6(>!%@(%')%)!3OP!($-?%!B6G%&L!

7B7BMB9 A"3+&'26D4&I!&645G%!&?$65(!(>-(!%@%?7(%&!%-?>!'6;>(!-')!6&&7%&!-!'74*%$!/B!T#<I:E!&(-(%4%'(&!A%'(!B$/4!(-J6';!-55$/@64-(%G+!a!46'7(%&!(/!\a!46'7(%&!*%(A%%'!)-6G+!%@%?7(6/'&L!:>6&!&?$65(!47&(!B6'6&>!*%B/$%!/(>%$!]/*&!&(-$(!6'!/$)%$!(/!4-J%!&7$%!(>%!5$/?%&&%&!(>-(!B/GG/A!5$/5%$G+!>-')G%!$%4/V6';!7'*6GG%)!%@?%5(6/'&!B/$!?7&(/4%$!)-(-!6'!(>%!&(-(%&!/B!i%A!Z/$JK!:%@-&!-')!i%A!.%@6?/L!:>%!&?$65(!A-&!645G%4%'(%)!-&!-!A/$J^-$/7')!B/$!-'!-55G6?-(6/'!*7;!-')!A6GG!$7'!'6;>(G+!7'(6G!(>%!'%%)%)!B6@%&!?-'!*%!4-)%!(/!(>%!-55G6?-(6/'L!

E-?>!'6;>(!A>%'!(>%!&?$65(!?/45G%(%&K!-!'/(6B6?-(6/'!%4-6G!6&!&%'(!(/!(>%!-55$/5$6-(%!&755/$(!(%-4&L!k/$!(>%!'6;>(!(>%!]/*!%@%?7(6/'!(64%!6'?$%-&%)K!(>%!%@%?7(6/'!(64%!A-&!'/(%)!6'!(>%!%4-6G!-')!-!>6;>!5$6/$6(+!5$/*G%4!(6?J%(!A-&!?$%-(%)!(/!-'-G+[%K!)6-;'/&%!-')!?/$$%?(!(>%!6&&7%L!:>%!)%V%G/54%'(!(%-4!A-&!'/(6B6%)!-')!$%X7%&(%)!(>%!<YI!(%-4!(/!>%G5!A6(>!(>%!$//(!?-7&%!-'-G+&6&L!

W!$%V6%A%)!(>%!&?$65(!-')!B/7')!(>-(!6(!6'?G7)%)!0aD!6')6V6)7-G!T#<I:E!&(-(%4%'(&!G6J%!(>6&H!

UPDATE TR_POLICY set POL_TYPE_CD = 1, audit_rec_updt_dts = SYSTIMESTAMP, audit_rec_updt_ver_nbr = audit_rec_updt_ver_nbr + 1, audit_rec_updt_appl_id = DCCR

WHERE REPORTED_PROP_STATE_CD = 'NY' AND RATE_POLICY_CD = '111' AND DECODE("POL_SUB_STAT_CD", 'UBE', 1, 'VALID', 2, 'EXCEPTION', 2, 0) = 1 AND POL_DATA_SOURCE_CD = 'UPLOAD';

E-?>!T#<I:E!V-$6%)!/'G+!*+!(>%!V-G7%!(/!*%!&%(!B/$!#FPj:Z#EjC<!-')!(>%!5$%)6?-(%!V-G7%&!B/$!SE#FS:E<j#SF#j3:I:EjC<!-')!SI:Ej#FPWCZjC<L!!

W!;$-**%)!(>%!%@%?7(6/'!)-(-!B$/4!IMS!_7&6';!(>%!&(-')-$)!-A$$5(L&XG`!-')!B/7')!(>-(K!/'!-V%$-;%K!%-?>!6')6V6)7-G!T#<I:E!(//J!-55$/@64-(%G+!"8^""!&%?/')&!(/!?/45G%(%L!d6V%'!(>-(!(>%!7&7-G!(64%!B/$!-!&6';G%!%@%?7(6/'!(/!?/45G%(%!(//J!-$/7')!-!&%?/')K!W!&7&5%?(%)!(>%!5$/*G%4!A-&!A6(>!&(-(6&(6?&!(>-(!4-+!>-V%!?-7&%)!(>%!%@%?7(6/'!5G-'!(/!?>-';%!B/$!(>%!A/$&%L!

W!(//J!(>%!3OPjW<!B$/4!(>%!$%5/$(!-')!X7%$6%)!IMS!7&6';!-!&?$65(!'-4%)!@5)-A$L&XG!(/!$%($6%V%!(>%!%@%?7(6/'!5G-'!B/$!/'%!/B!(>%!T#<I:E&!)7$6';!(>%!N&G/AQ!(64%L!W!-G&/!57GG%)!-'!%@%?7(6/'!5G-'!B/$!(>%!&-4%!3OPjW<!B$/4!-!5$6/$!%V%'6';!A>%'!6(!>-)!?/45G%(%)!6'!(>%!7&7-G!0!46'7(%!A6')/AL!:>%!(A/!5G-'&!A%$%!X76(%!)6BB%$%'(L!2%$%U&!(>%!*-&6?!N*-)Q!5G-'!B/GG/A%)!*+!(>%!N;//)Q!5G-'H!

Page 10: Managing SQL Performance

! ! !

!

! ! ! ! ! ! ! ! ! ! !"8!

Y-)!5G-'!

---------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| ---------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 4 | 1472 | | 287K (3)| |* 1 | TABLE ACCESS BY INDEX ROWID | TR_POLICY | 4 | 1472 | | 287K (3)| | 2 | BITMAP CONVERSION TO ROWIDS | | | | | | | 3 | BITMAP AND | | | | | | | 4 | BITMAP CONVERSION FROM ROWIDS| | | | | | | 5 | SORT ORDER BY | | | | 37M| | |* 6 | INDEX RANGE SCAN | TR_POLICY_N5 | | | | 6543 (1)| | 7 | BITMAP CONVERSION FROM ROWIDS| | | | | | | 8 | SORT ORDER BY | | | | 95M| | |* 9 | INDEX RANGE SCAN | TR_COUNTY_FK6 | | | | 8721 (1)| ---------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("RATE_POLICY_CD"='111' AND "POL_DATA_SOURCE_CD"='UPLOAD') 6 - access(DECODE("POL_SUB_STAT_CD",'UBE',1,'VALID',2,'EXCEPTION',2,0)=1) filter(DECODE("POL_SUB_STAT_CD",'UBE',1,'VALID',2,'EXCEPTION',2,0)=1) 9 - access("REPORTED_PROP_STATE_CD"='NY') filter("REPORTED_PROP_STATE_CD"='NY')

!

d//)!5G-'!

---------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes| Cost (%CPU)| ---------------------------------------------------------------------------------- |* 1 | TABLE ACCESS BY INDEX ROWID| TR_POLICY | 1 | 368 | 7307 (1)| |* 2 | INDEX RANGE SCAN | TR_POLICY_N5 | 16711 | | 92 (0)| ---------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter(("RATE_POLICY_CD"='111' AND "POL_DATA_SOURCE_CD"=AND "REPORTED_PROP_STATE_CD"='NY')) 2 - access("TR_POLICY"."SYS_NC00063$"=1)

!

M6(>!]7&(!(>6&!5G-'!)-(-K!(>%!&(-(6&(6?&!-$%!)%B6'6(%G+!G//J6';!G6J%!(>%!?7G5$6(L!W'!(>%!;//)!5G-'K!(>%!6')%@!$-';%!&?-'!/'!:Sj#FPWCZjia!>-&!-!?/&(!/B!m0!-')!%&(64-(%)!/'G+!"DKc""!$/A&!(/!*%!$%(7$'%)L!Y7(!6'!(>%!*-)!5G-'K!(>%!&-4%!&?-'!?/&(&!Da\1!-')!%&(64-(%)!(>-(!1c!46GG6/'!$/A&!A/7G)!*%!$%(7$'%)L!W(!&%%4&!6(U&!(64%!(/!?>%?J!(>%!&(-(6&(6?&L!Y%?-7&%!/7$!&(-(6&(6?&!?/GG%?(6/'!6&!6'&($74%'(%)K!W!?-'!%-&6G+!?>%?J!(/!&%%!A>%'!&(-(6&(6?&!A%$%!G-&(!?/GG%?(%)!B/$!-!5-$(6?7G-$!/*]%?(L!M%UGG!(-GJ!4/$%!-*/7(!6'&($74%'(-(6/'!&>/$(G+K!&/!WUGG!'/(!;/!6'(/!4/$%!)%(-6G!/'!(>-(!'/AL!I!X76?J!?>%?J!&>/A%)!4%!(>-(!(>%!(-*G%!*%6';!75)-(%)K!:Sj#FPWCZK!>-)!&(-(6&(6?&!?/GG%?(%)!/'!6(!-(!DH"a54!(>%!%V%'6';!5$6/$!(/!A>%'!(>6&!&?$65(U&!%@%?7(6/'!(64%!6'?$%-&%)L!

W!>-V%!-!&?$65(!?-GG%)!&(^-GGL&XG!(>-(!6&!-!?/45/&6(%!&?$65(!(/!?-GG!&%V%$-G!)6BB%$%'(!&(-(6&(6?&!?/GG%?(6/'!X7%$+!&?$65(&L!W!?-'!$7'!(>6&!&6';G%!V%$&6/'!(/!;%(!-GG!(>%!&(-(&!B/$!-!&5%?6B6%)!/*]%?(!_(-*G%!&(-(&K!?/G74'!&(-(&K!6')%@!&(-(&K!%(?L`!/$!W!?-'!$7'!-!&?$65(!B/$!]7&(!(>%!&(-(&!W!A-'(!(/!&%%L!M>-(!W!A-'(%)!(/!J'/A!A-&!(>%!&(-(&!B/$!(>%!?/G74'&!7&%)!6'!(>%!:Sj#FPWCZjia!6')%@L!I&!+/7!?-'!+/7!&%%!B$/4!(>%!5$%)6?-(%!6'B/$4-(6/'!6'!(>%!5G-'K!(>%!?/G74'!WU4!G//J6';!B/$!6&!3Z3jiC888D1hL!.+!&?$65(!&>/A%)!4%!(>%!B/GG/A6';!56%?%&!/B!6'B/$4-(6/'H!

========================================================================================================= INDEX STATISTICS ========================================================================================================= Index Name Pos# Order Column Name -------------- ---- ----- ------------- tr_policy_n5 1 ASC sys_nc00063$ 2 ASC sys_nc00064$ 3 ASC pol_sys_id 4 ASC batch_sys_id

Page 11: Managing SQL Performance

! ! ! ! !! !

! ! ! ! ""!

Index Name Column Name Pos# Expression -------------- -------------- ---- -------------------------------------------------------------- tr_policy_n5 SYS_NC00063$ 1 DECODE("POL_SUB_STAT_CD",'UBE',1,'VALID',2,'EXCEPTION',2,0) SYS_NC00064$ 2 TRUNC("ENTERED_INTO_SYS_DTS") ========================================================================================================= COLUMN STATISTICS ========================================================================================================= Name Analyzed Null? NDV Density # Nulls # Buckets Sample AvgLen Lo-Hi Values ========================================================================================================= sys_nc00063$ 02/11/2011 18:15:53 Y 2 .000000 0 2 6953 3 0 | 2 =========================== HISTOGRAM STATISTICS =========================== SYS_NC00063$ (2 buckets) 1 97%

3/K!(>%!&(-(&!B/$!(>%!?/G74'!&>/A!(>-(!(>%$%!-$%!(A/!)6&(6'?(!V-G7%&!_8K!0`!-')!(>-(!/'%!/B!(>%!V-G7%&!6&!5$%&%'(!6'!mcn!5%$?%'(!/B!(>%!$/A&L!Y7(K!)6)'U(!(>%!5$%)6?-(%!B/$!(>%!T#<I:E!7&%!-!V-G7%!/B!"R!Z%&f!3/K!6(!G//J&!G6J%!(>%!V-G7%!"!6&!46&&6';!B$/4!(>%!&(-(&L!W'!/$)%$!(/!?/'B6$4!(>6&!?>-';%!A-&!4-)%!)7%!(/!(>%!&(-(6&(6?&!?/GG%?(6/'K!W!'%%)%)!(/!?/45-$%!(>%!?7$$%'(!&(-(&!(/!(>%!5$%V6/7&!?/GG%?(6/'L!F$-?G%!-7(/4-(6?-GG+!*-?J&!75!&(-(6&(6?&!5$6/$!(/!?/GG%?(6';!-!'%A!&%(!&/!W!?/7G)!57GG!(>%4!B$/4!(>%!>6&(/$+L!36'?%!A%!-G&/!J%%5!*-?J75&!/B!/7$!/A'K!W!/5(%)!(/!?/45-$%!(/!/7$!*-?J75!?/5+L!W!7&%)!-'/(>%$!&?$65(!?-GG%)!)6BB^&(-(&^&(-((-*L&XG!(>-(!%@%?7(%&!-!)6BB%$%'?%!$%5/$(!7&6';!(>%!<Y.3j3:I:3L<Wkkj:IYPEj3:I:3jWij3:I::IY!5$/?%)7$%L!2%$%!-$%!(>%!J%+!56%?%&!/B!6'B/$4-(6/'!W!>-)!-B(%$!%@%?7(6';!4+!&?$65(H!

STATISTICS DIFFERENCE REPORT FOR: ................................. TABLE : TR_POLICY OWNER : TRAX_OWNER SOURCE A : User statistics table TRAX_STATS_BACKUP_20110211 : Statid : : Owner : TRAX_OWNER SOURCE B : Current Statistics in dictionary PCTTHRESHOLD : 1 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ COLUMN STATISTICS DIFFERENCE: ............................. COLUMN_NAME SRC NDV DENSITY HIST NULLS LEN MIN MAX SAMPSIZ ............................................................................... SYS_NC00063$ A 3 .000000009 YES 0 3 80 C103 5.5E+07 B 2 .000000008 YES 0 3 80 C103 6953

!

! i/(6?%!(>-(!(>%!i<g!_'74*%$!/B!)6&(6'?(!V-G7%&`!?>-';%)!B$/4!1!(/!0!A>%'!(>%!&(-(&!A%$%!?/GG%?(%)L!WB!+/7!G//J!-!*6(!?G/&%$K!+/7UGG!'/(6?%!(>%!$%-&/'!A>+L!:>%!&-45G%!&6[%!B/$!(>%!5$%V6/7&!?/GG%?(6/'!A-&!)/'%!-(!"88n!_'/(%!(>%!&-45G%!&6[%!V-G7%!/B!aLaEo8c`!-&!?/45-$%)!(/!-!&-45G%!&6[%!/B!]7&(!7')%$!c888!$/A&!B/$!(>%!G-(%&(!?/GG%?(6/'L!I55-$%'(G+K!(>%!&4-GG%$!&-45G%!A-&'U(!*$/-)!%'/7;>!(/!?-5(7$%!-'+!/B!(>%!$/A&!(>-(!>-)!-!V-G7%!/B!"!B/$!(>-(!?/G74'L!3/K!(>%!>6&(/;$-4!/'G+!>-)!(A/!*7?J%(&K!/'%!B/$!8!V-G7%&!-')!/'%!B/$!0!V-G7%&L!36'?%!-!"U&!*7?J%(!A-&!46&&6';K!(>%!/5(646[%$!?/457(%)!&%G%?(6V6(+!B/$!(>%!7'J'/A'!V-G7%!*-&%)!/'!>-GB!(>%!46'6474!J'/A'"!?-$)6'-G6(+!V-G7%&!-$$6V6';!-(!-!$/A&!%&(64-(%!/B!1c!46GG6/'L!:>6&!%&(64-(%!?-7&%)!(>%!/5(646[%$!(/!?>//&%!-!&7*^/5(64-G!YW:.I#!CFigES3WFi!5G-'!/5%$-(6/'!(/!($+!-')!$%)7?%!(>%!%BB/$(!(/!&?-'!1c!46GG6/'!$/A&!*+!)/6';!-!YW:.I#!Ii<!A6(>!-'/(>%$!6')%@!/'!(>%!&(-(%!?/G74'!_&(-(%!A-&!-G&/!7&%)!6'!(>%!5$%)6?-(%`L!!

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"!3%%!b/'-(>-'!P%A6&U!*G/;!%'($6%&!/'!(>6&!&7*]%?(!-(!4))56778(%")4"%-$2,3&2(#15#$33&.('79::;7:<79=7>#$?@$%.AB4,3)(+#"'37!-')!4))56778(%")4"%-$2,3&2(#15#$33&.('79:C:7:D79D7>"!$B4,3)(+#"'37L!!

Page 12: Managing SQL Performance

! ! !

!

! ! ! ! ! ! ! ! ! ! !"0!

! 36'?%!(>%!$%-G6(+!A-&!(>-(!(>%$%!A%$%!V-G7%&!/B!"!6'!(>-(!?/G74'K!(>-(!6'B/$4-(6/'!'%%)%)!(/!*%!-V-6G-*G%!(/!(>%!/5(646[%$!6'!/$)%$!B/$!6(!(/!*%!-*G%!(/!4-J%!(>%!5$/5%$!&%G%?(6V6(+!%&(64-(%&L!M>-(!)/!A%!)/R!M%GGK!A%!?/7G)!?/GG%?(!&(-(&!-GG!/V%$!-;-6'!-(!"88nL!:>-(U&!5$%?6&%G+!A>-(!>-)!*%%'!)/'%!6'!(>%!5$%V6/7&!?/GG%?(6/'L!Y7(K!)/6';!&/!A/7G)!(-J%!&%V%$-G!>/7$&!-')!A/7G)!*7$'!-!G/(!/B!$%&/7$?%&!-')!$%?/GG%?(!&(-(&!/'!-GG!(>%!/(>%$!?/G74'&!(>-(!$%-GG+!)6)'U(!'%%)!(/!*%!$%?/GG%?(%)L!!

! S-(>%$!(>-'!A-&(%!-GG!(>-(!(64%!-')!%BB/$(K!W!/5(%)!(/!4-'7-GG+!&%(!(>%!&(-(6&(6?&!(/!(>%!V-G7%&!W!J'%A!A%$%!'%%)%)L!W'!(>6&!?-&%K!W!J'%A!W!'%%)%)!(/!*745!75!(>%!'74*%$!/B!)6&(6'?(!V-G7%&!(/!1!-')!&%(!(>%!-55$/5$6-(%!V-G7%&!B/$!(>%!46&&6';!*7?J%(!6'!(>%!>6&(/;$-4L!P7?J6G+K!W!>-)!-'/(>%$!&?$65(!(>-(!-GG/A%)!4%!(/!4-J%!(>/&%!4/)6B6?-(6/'&!X76?JG+!_&%(>&(-(&L&XGK!&>/$(!B/$!&%(!>6&(/;$-4!&(-(&`L!!

! M6(>!(>%!'%A!&(-(&!6'!5G-?%K!W!(%&(%)!(>%!75)-(%!-;-6'!-')!A-&!5G%-&%)!(>-(!%V%$+(>6';!A-&!*-?J!(/!'/$4-Gf!:>%!%'(6$%!5$/?%&&!(//J!4%!-*/7(!"8!46'7(%&!-')!4/&(!/B!(>-(!A-&!&5%'(!)/?74%'(6';!A>-(!W!A-&!)/6';!-&!W!A%'(!-G/';!5G7&!(-J6';!(64%!(/!)/7*G%^?>%?J!4+&%GB!(>$/7;>!%-?>!&(%5L!Y7(K!(>%!*/((/4^G6'%!A-&!(>-(!W!>-)!(//G&!_&?$65(&!6'!(>6&!?-&%`!$%-)6G+!-(!>-')!(>-(!W!?/7G)!7&%!(/!;%(!(>%!6'B/$4-(6/'!W!'%%)%)!(/!6)%'(6B+!(>%!$//(!?-7&%!/B!(>%!5$/*G%4!-')!(>%'!)%(%$46'%!(>%!B6@!'%%)%)!(/!?/$$%?(!6(L!

! I&!6(!(7$'&!/7(K!(>%!&?$65(!%')%)!75!;%((6';!-!B7$(>%$!B-?%^G6B(!(/!?>-';%!(>%!0aD!6')6V6)7-G!T#<I:E!&(-(%4%'(&!(/!-!*7GJ!/5%$-(6/'!-')!(/!-))!&/4%!6'&($74%'(-(6/'!(/!>%G5!7&!;%(!(/!(>%!$//(!/B!(>%!5$/*G%4!%V%'!B-&(%$!6B!(>6';&!A%'(!&/7(>!-;-6'!6'!(>%!B7(7$%L!:>%!?>-';%!4-)%!(/!&>6B(!(/!7&6';!-!*7GJ!/5%$-(6/'!6&!-?(7-GG+!/'%!/B!(>%!-'(65-((%$'&!A%UGG!)6&?7&&!G-(%$L!

7BM >+I+"N%#$&2H+&!"$%0&

M$6(6';!-')!4-6'(-6'6';!3OP!(>-(!5%$B/$4&!A%GG!/B(%'!&%%4&!G6J%!-!4-;6?!($6?JL!M>%'!+/7!A-(?>!&/4%/'%!A/$J!(>-(!+/7!5%$?%6V%!(/!*%!-!3OP!4-;6?6-'K!(>%+!-55%-$!(/!*%!-*G%!(/!4-J%!(>%!645/&&6*G%!>-55%'!-')!B6')!-')!B6@!3OP!5%$B/$4-'?%!6&&7%&!A6(>!G6((G%!%BB/$(L!Y7(K!6&!6(!$%-GG+!4-;6?!/$!6&!6(!]7&(!-!A%GG^$%>%-$&%)!&%(!/B!&(%5&!-')!-?(6/'&!(>-(!BG/A!(/;%(>%$!%BB/$(G%&&G+!(/!5$/)7?%!-!B6'-G!$%&7G(R!

! Z/7!-G$%-)+!J'/A!(>%!-'&A%$l!6(U&!'/(!4-;6?L!Y7(K!G%-$'6';!>/A!(/!4-J%!6(!-55%-$!G6J%!4-;6?!(-J%&!]7&(!-&!47?>!5$-?(6?%!-&!-'+!4-;6?6-'!A/7G)!7&%!(/!*%?/4%!5$/B6?6%'(!-(!>6&!?$-B(L!i/(!%V%$+/'%!4-+!$%-?>!(>%!5$/B6?6%'?+!G%V%G!/B!-!2-$$+!2/7)6'6!/$!<-V6)!YG-6'%!_B/$!7&!F$-?G%!;%%J&K!A%!46;>(!&-+!:/4!,+(%!/$!b/'-(>-'!P%A6&`K!*7(!%V%$+/'%!?-'!G%-$'!A>-(!(>%+!'%%)!(/!57GG!/BB!(>%6$!($6?J&!4/&(!/B!(>%!(64%L!W(!-GG!&(-$(&!A6(>!-!B%A!*-&6?&L!

7BMB9 '()&.52%/%O"2%.#&P"3%03&

Z/7U$%!'/(!;/6';!-'+A>%$%!(/A-$)&!*%?/46';!5$/B6?6%'(!-(!4-'-;6';!3OP!5%$B/$4-'?%!7'(6G!+/7!G%-$'!-!*6(!/B!(>%!N7')%$!(>%!?/V%$&Q!*-&6?&L!:>%!)-(-*-&%!6&!'/(!-!*G-?J!*/@K!'/!4-((%$!>/A!>-$)!A%!4-+!A6&>!6(!A%$%L!W'!/$)%$!(/!A$6(%!*%((%$!3OP!-')!($/7*G%^&>//(!3OP!5%$B/$4-'?%!6&&7%&K!+/7!>-V%!(/!7')%$&(-')!>/A!3OP!6&!B/$47G-(%)!-')!>/A!6(!%@%?7(%&L!C/V%$6';!-GG!(>%!)%(-6G&!/B!(>%!*-&6?&!+/7!'%%)!(/!?/45$%>%')!6&!*%+/')!(>%!&?/5%!/B!A>-(!A%!?-'!?/V%$!6'!/'%!)-+K!*7(!>%$%U&!-!G6&(!/B!5$/B6?6%'?6%&!+/7!'%%)H!

'()&34#2"Q&

i/(!/'G+!)/!+/7!'%%)!(/!J'/A!;%'%$-G!&(-(%4%'(!&+'(-@K!*7(!(>%!B7$(>%$!+/7!?-'!%@5-')!+/7$!>/$6[/'&!(/!G%-$'!A-+&!(/!B/$47G-(%!3OP!*%+/')!*-&6?!3EPEC:!p!kSF.!:IYPE!&(-(%4%'(&K!(>%!4/$%!(//G&!+/7UGG!>-V%!A>%'!B-?%)!A6(>!-!3OP!(7'6';!?>-GG%';%L!Z/7!'%%)!(/!*%?/4%!B-46G6-$!A6(>!*76G(^6'!B7'?(6/'&K!-')!

Page 13: Managing SQL Performance

! ! ! ! !! !

! ! ! ! "1!

75)-(%!+/7$!J'/AG%);%!A6(>!%-?>!'%A!$%G%-&%L!F$-?G%!A6GG!/B(%'!-))!-!B7'?(6/'!(>-(!+/7!4-+!>-V%!*%%'!?/)6';!A/$J-$/7')&!B/$!+%-$&!_B/$!%@-45G%K!(>%!#WgF:!B7'?(6/'`L!

'()&+Q+062%.#&P"3%03&

M>%'!+/7!&7*46(!-!3OP!&(-(%4%'(K!A>-(!>-55%'&R!Z/7!'%%)!(/!J'/AL!:>6&!4%-'&!+/7UV%!;/(!(/!*%?/4%!B-46G6-$!A6(>!>-$)!-')!&/B(!5-$&6';K!(>%!&>-$%)!5//GK!(>%!*7BB%$!?-?>%K!G/;6?-G!-')!5>+&6?-G!WFK!-')!4/$%L!WB!+/7!)/'U(!7')%$&(-')!>/A!+/7$!3OP!6&!%@%?7(%)K!>/A!?-'!+/7!%@5%?(!(/!%BB%?(6V%G+!B6;7$%!/7(!>/A!(/!?>-';%!6(&!*%>-V6/$R!

:H+&.52%/%O+,&

:>%!?/&(^*-&%)!/5(646[%$!'%%)&!(/!*%?/4%!+/7$!B$6%')L!:>%!/5(646[%$!6&!$%&5/'&6*G%!B/$!)%(%$46'6';!>/A!+/7$!3OP!&(-(%4%'(&!A6GG!%@%?7(%!&/!+/7!'%%)!(/!7')%$&(-')!6(!%'/7;>!(/!J'/A!>/A!(/!;6V%!6(!A>-(!6(!'%%)&!(/!)/!6(&!]/*!-&!%BB%?(6V%G+!-&!5/&&6*G%L!:>6&!4%-'&!+/7!>-V%!(/!&5%-J!(>%!&-4%!G-';7-;%L!S%-)6';!%@%?7(6/'!5G-'&!>-&!(/!*%?/4%!-&!B-46G6-$!-')!%-&+!(/!+/7!-&!$%-)6';!+/7$!B-V/$6(%!4-;-[6'%L!Z/7UV%!;/(!(/!7')%$&(-')!&(-(6&(6?&L!!Z/7UV%!;/(!(/!7')%$&(-')!(>%!V-$6/7&!/5%$-(6/'&!(>%!/5(646[%$!?-'!?>//&%!(/!7&%!B/$!+/7$!3OPU&!%@%?7(6/'L!Z/7!*-&6?-GG+!>-V%!(/!G%-$'!(/!(>6'J!G6J%!(>%!/5(646[%$L!!

C#"N43%3&3R%NN3&

Z/7!4-+!>-V%!(>%!J'/AG%);%!_W!?-GG!6(!N*//J!&4-$(&Q`K!*7(!A>%'!6&&7%&!-$6&%!+/7!>-V%!(/!*%!-*G%!(/!?/GG%?(!(>%!J%+!56%?%&!/B!6'B/$4-(6/'!-')!57(!6(!-GG!(/;%(>%$!(/!5$/5%$G+!-'-G+[%!-')!)6-;'/&%!(>%!5$/*G%4L!:>6&K!6'!4+!/56'6/'K!6&!(>%!*6;;%&(!5-$(!/B!(>%!N4-;6?LQ!,'/A6';!&/4%(>6';!-')!-55G+6';!A>-(!+/7!J'/A!-$%!(A/!)6BB%$%'(!(>6';&L!W(U&!(>%!-55G6?-(6/'!/B!(>%!J'/AG%);%!+/7!/*(-6'!(>-(!6&!(>%!J%+L!

WU4!;/6';!(/!-&&74%!+/7UV%!;/(!4/&(!/B!(>%&%!*-&%&!?/V%$%)K!/$!+/7!&//'!A6GGK!-&!A%!5$/;$%&&!(>$/7;>!(>%!$%&(!/B!(>%!4-(%$6-GL!3/K!6B!W!&(-$(!(-GJ6';!-*/7(!-'+(>6';!+/7U$%!7'B-46G6-$!A6(>!/$!'%%)!(/!$%B$%&>!+/7$!4%4/$+!-*/7(K!WUGG!($7&(!+/7!(/!)/!(>-(!-&!'%%)%)L!

7BMB9B9 :6#%#$&'()&.+!]/*!6'V/GV%&!-!G/(!/B!3OP!(7'6';!_W!4%-'!-!G/(f`L!WUV%!*%%'!)/6';!6(!B/$!&/!G/';!(>-(!A>-(!(/!)/!A>%'!WU4!5$%&%'(%)!A6(>!-!&(-(%4%'(!(>-(!'%%)&!(7'6';!>%G5!6&!4/&(G+!&%?/')!'-(7$%L!Y7(K!6B!W!&(/5!-')!(>6'J!-*/7(!(>%!&(%5&!6'V/GV%)!6'!(7'6';!-!3OP!&(-(%4%'(K!6(U&!(+56?-GG+!5$%((+!&($-6;>(B/$A-$)L!!!

• S%V6%A!(>%!5$/*G%4!3OP!&(-(%4%'(L!

• k6')!/7(!A>-(!6(U&!&755/&%)!(/!)/L!_:7'%!(>%!X7%&(6/'K!'/(!(>%!X7%$+L`!

• E@%?7(%!(>%!&(-(%4%'(!-')!?-5(7$%!%@%?7(6/'!5G-'!)-(-!_6B!6(U&!5$/)7?(6/'K!;%(!IMS=I32=3(-(&#-?J=WPF!)-(-K!%@%?7(%!6'!5$/)!6B!5/&&6*G%!/$!6'!5$/)7?(6/'^G6J%!%'V6$/'4%'(`L!

• S%V6%A!(>%!&(-(6&(6?&!B/$!(>%!/*]%?(&!7&%)!6'!(>%!&(-(%4%'(L!C>%?J!6')%@%&!-')!?/'&($-6'(&L!

• EV-G7-(%!(>%!%@%?7(6/'!5G-'K!?/45-$%!%&(64-(%&!V&!-?(7-G!_?-$)6'-G6(+!B%%)*-?J`L!

• P//J!B/$!(>%!*6;!>6((%$&!_5G-?%&!6'!(>%!5G-'!A>%$%!(>%!4/&(!(64%!6&!&5%'(`L!!

• S%B-?(/$!(>%!&(-(%4%'(L!

• :%&(!$%A$6((%'!3OP!-')!?/45-$%!(/!/$6;6'-GL!

• S%B-?(/$!-')!$%5%-(!7'(6G!A6(>6'!3PI!/$!'/!B7$(>%$!(7'6';!6&!5/&&6*G%L!

!

Page 14: Managing SQL Performance

! ! !

!

! ! ! ! ! ! ! ! ! ! !"\!

W(U&!-G&/!-!;//)!6)%-!(/!J%%5!-'!%+%!/7(!B/$!?%$(-6'!(>6';&!(>-(!?-'!>%G5!4-J%!+/7$!(7'6';!(-&J&!%-&6%$L!2%$%U&!-!&>/$(!G6&(!/B!?/44/'!(>6';&!W!G//J!B/$!/$!)/!A>%'!WU4!(7'6';!3OPH!

• C/'&($-6'(&!-$%!-!47&(f!#$64-$+!J%+&K!B/$%6;'!J%+&K!?>%?J!?/'&($-6'(&!-$%!-GG!%@($%4%G+!645/$(-'(!(/!(>%!/5(646[%$U&!-*6G6(+!(/!5$/)7?%!-!X7-G6(+!5G-'L!M6(>/7(!(>%4K!(>%!/5(646[%$!]7&(!?-'U(!)/!6(&!]/*!-&!%BB6?6%'(G+!-&!6(!?-'!A6(>!(>%4L!

• Z/7UV%!;/(!(/!J'/A!(>%!)-(-!-')!(>%!&?>%4-L!WB!+/7!)/'U(!J'/AK!-&J!X7%&(6/'&!/B!&/4%/'%!A>/!)/%&L!

• P//J!B/$!(-*G%&!(>-(!-$%!7&%)!6'!]/6'&!*7(!5$/V6)%!'/!?/G74'&!6'!(>%!/7(57(L!:>%&%!?-'!/B(%'!*%!%G646'-(%)L!

• P//J!B/$!$%5%-(%)!7&%!/B!(>%!&-4%!(-*G%&!7&6';!(>%!&-4%K!/$!&646G-$K!5$%)6?-(%&L!:>%&%!?-'!/B(%'!*%!$%)7?%)!(/!7&%!/'?%L!

• C>%?J!-V-6G-*G%!6')%@%&!-')!%V-G7-(%!(>%6$!V6-*6G6(+!-;-6'&(!5$%)6?-(%&L!

• P//J!B/$!7&%!/B!<W3:WiC:!/$!TiWFi!(/!$%4/V%!)75G6?-(%&L!!

!

W'!(>%!%')K!+/7$!;/-G!6'!4-'-;6';!3OP!5%$B/$4-'?%!6&!$%-GG+!(A/^B/G)H!A$6(%!B-&(!3OP!B-&(%$!-')!B6')!-')!B6@!*-)!3OP!B-&(L!:>%$%!6&'U(!$%-GG+!-'+!4-;6?L!:>%!4-;6?!6&!6'!>/A!A6GG6';!+/7!-$%!(/!G%-$'!-')!5$-?(6?%L!

M <=':>@!F=:C:<?=&

qWB!+/7!?-'U(!4%-&7$%!6(K!+/7!?-'U(!4-'-;%!6(Lq!9!<-V6)!d-$V6'!

!

W!G/V%!(>6&!X7/(%!*%?-7&%!6(!&5%-J&!(/!(>%!>%-$(!/B!4-'-;6';!5%$B/$4-'?%L!W'&($74%'(-(6/'!6&!-!G6((G%!%@($-!?/)%!(>-(!)%V%G/5%$&!57(!6'(/!(>%6$!-55G6?-(6/'&!(>-(!(%GG&!%V%$+/'%!r!6'?G7)6';!(>%!&/B(A-$%!)%V%G/5%$&!(>%4&%GV%&!r!%@-?(G+!A>%$%!+/7$!&/B(A-$%!6&!&5%')6';!+/7$!(64%L!M6(>!6(K!4-'-;6';!5%$B/$4-'?%!6&!-!&'-5L!

d//)!6'&($74%'(-(6/'!4-J%&!6(!&/!'/$4-G!5%/5G%K!'/(!]7&(!&5%?6-G6&(&K!?-'!)6-;'/&%!-')!&/GV%!5%$B/$4-'?%!5$/*G%4&!X76?JG+!-')!5%$4-'%'(G+K!/B(%'!*%B/$%!+/7$!7&%$&!%V%'!&%'&%!&/4%(>6';!6&!A$/';L!2/A%V%$K!A6(>/7(!;//)!(64%^*-&%)!5%$B/$4-'?%!6'&($74%'(-(6/'K!4-'-;6';!5%$B/$4-'?%!*%?/4%&!'6;>(4-$6&>G+!?/45G%@!-')!%@5%'&6V%L!

M6(>!-&!G6((G%!-&!(A/!G6'%&!/B!?/)%!+/7!A6GG!*%!-*G%!(/!4/'6(/$!&5%?6B6?!(-&J&!6'!+/7$!-55G6?-(6/'!-')!4-J%!/'%!/B!(>%!4/&(!)6BB6?7G(!&(%5&!/B!645G%4%'(6';!$%&5/'&%^(64%!*-&%)!5$/*G%4!)6-;'/&6&!_.%(>/)!S`!6'(/!/'%!/B!(>%!%-&6%&(L!W'!-))6(6/'K!(>%&%!(%?>'6X7%&!A6GG!%'-*G%!+/7!(/!?/$$%G-(%!)-(-*-&%!&(-(6&(6?&!*-?J!(/!*7&6'%&&!(-&J&!-')!>%G5!)6&?/V%$!(>/&%!(-&J&!(>-(!-$%!>%-V+!$%&/7$?%!?/'&74%$&L!

MB9 SH4&%3&/4&5,.$,"/&3N.KT&

M>%'!6&!&/4%(>6';!&G/AR!M%!-$%!(+56?-GG+!4-)%!-A-$%!/B!&G/A'%&&!*+!-!7&%$!?/45G-6'(L!3G/A!6&!$%G-(6V%!>/A%V%$L!WB!+/7!&-+!6(!(-J%&!"!>/7$!(/!;%(!B$/4!5/6'(!I!(/!5/6'(!YK!?/7G)!+/7!-'&A%$!(>%!N6&!(>6&!&G/AQ!X7%&(6/'!A6(>!/'G+!(>-(!*6(!/B!6'B/$4-(6/'R!i/L!Z/7!'%%)!(/!-(!G%-&(!J'/A!A>-(!(>%!)6&(-'?%!6&!B$/4!5/6'(!I!(/!5/6'(!Y!-')!>-V%!-'!6)%-!/B!(>%!$%-&/'-*G%!%@5%?(-(6/'!B/$!>/A!G/';!6(!&>/7G)!(-J%!(/!;%(!B$/4!/'%!5/6'(!(/!(>%!/(>%$L!WB!(>%!)6&(-'?%!6&!>7')$%)&!/$!(>/7&-')&!/B!46G%&K!"!>/7$!46;>(!*%!V%$+!$%-&/'-*G%K!*7(!6B!(>%!)6&(-'?%!6&!08!B%%(K!"!>/7$!4-+!*%!%@($-/$)6'-$6G+!G/';L!

Page 15: Managing SQL Performance

! ! ! ! !! !

! ! ! ! "a!

:/!)%(%$46'%!A>+!-!5$/;$-4!6&!&G/AK!G%(U&!&(-$(!*+!A-GJ6';!(>$/7;>!(>%!-'-G+&6&!/B!-!5$/?%)7$%!'-4%)!#L!#!6&!$7''6';!3-(2L!3/K!B6$&(!W!'%%)!(/!J'/A!%@-?(G+!A>-(!&G/A!4%-'&!6'!(%$4&!/B!$%&5/'&%!(64%L!

SQL> set timing on SQL> exec p PL/SQL procedure successfully completed. Elapsed 00:02:09:98

F'?%!W!>-V%!-!*-&%!(646';!/B!0!46'7(%&!mLme!&%?/')&K!W!?-'!/5%'!75!(>%!?/)%!-')!($+!-')!)%(%$46'%!A>%$%!(/!;/!B$/4!(>%$%L!:>%!?/)%!B/$!#!6&H!

create or replace procedure p as begin q; r; s; end; /

i/A!A>-(R!#!?-GG&!(>$%%!/(>%$!5$/?%)7$%&L!3/K!>/A!)/!W!J'/A!A>%$%!(/!&(-$(R!M%GGK!W!?/7G)!&645G+!/5%'!75!(>%!B6$&(!5$/?%)7$%K!OK!-')!&%%!6B!W!?-'!B6')!-'+!645$/V%4%'(&!(/!*%!4-)%!(>%$%L!Y7(K!4-+*%!6(!A/7G)!*%!>%G5B7G!(/!*%!-!G6((G%!4/$%!6'B/$4%)!*%B/$%!W!&(-$(!($+6';!(/!G//J!B/$!-$%-&!(/!645$/V%L!2%$%U&!A>%$%!6'&($74%'(-(6/'!?/4%&!6'L!WUGG!-))!-!G6((G%!?/)%!(/!>%G5!4%!4%-&7$%!A>%$%!(>%!%@%?7(6/'!/B!#!&5%')&!6(U&!(64%H!

create or replace procedure p as t0 number; t1 number; t2 number; t3 number; begin t0 := dbms_utility.get_time; q; t1 := dbms_utility.get_time ; dbms_output.put_line ('Procedure q: ' || to_char((t1 - t0)/100)); r; t2 := dbms_utility.get_time ; dbms_output.put_line ('Procedure r: ' || to_char((t2 - t1)/100)); s; t3 := dbms_utility.get_time ; dbms_output.put_line ('Procedure s: ' || to_char((t3 - t2)/100)); dbms_output.put_line ('Total R : ' || to_char((t3 - t0)/100)); end; /

!

i/A!(>-(!WUV%!-))%)!&/4%!?/)%!(/!>%G5!?-5(7$%!(646';!)%(-6G!-(!-!4/$%!;$-'7G-$!G%V%GK!G%(U&!%@%?7(%!#!-;-6'L!

SQL> set timing on SQL> exec p Procedure q: 1 Procedure r: 114 Procedure s: 15 Total R : 130 Elapsed 00:02:09:99

! M6(>!(>6&!6'B/$4-(6/'K!'/A!W!J'/A!A>%$%!(>%!(64%!6&!&5%'(L!:>%!4-]/$6(+!/B!(64%!6&!&5%'(!%@%?7(6';!5$/?%)7$%!SL!:>-(U&!;$%-(!6'B/$4-(6/'!(/!>-V%L!Y7(K!A>-(!)6)!6(!?/&(!B/$!4%!(/!>-V%!(>6&!6'B/$4-(6/'R!i/(!47?>!$%-GG+L!:>%!(/(-G!%@%?7(6/'!(64%!6'?$%-&%)!*+!L8"!&%?/')&!_B$/4!"0mLme!(/!"0mLmm`L!W!>-)!(/!4-J%!&/4%!-))6(6/'&!(/!(>%!?/)%H!\!'%A!V-$6-*G%&!-')!e!G6'%&!/B!?/)%!(/!?-5(7$%!(646';!)-(-L!:>%!&4-GG!%BB/$(!'%((%)!*6;!$%&7G(&!6'!(>-(!'/A!W!J'/A!A>%$%!(/!B/?7&!4+!-((%'(6/'L!

Page 16: Managing SQL Performance

! ! !

!

! ! ! ! ! ! ! ! ! ! !"D!

! M>%$%!A/7G)!+/7!>-V%!&(-$(%)!6B!+/7!)6)'U(!>-V%!(>6&!)-(-R!./$%!645/$(-'(G+K!A>-(!A/7G)!>-55%'!6B!+/7!&(-$(%)!(/!A/$J!/'!5$/?%)7$%!O!/$!3!B6$&(R!Y-&%)!/'!(>%!(646';!)-(-!?/GG%?(%)K!%V%'!6B!A%!?/7G)!$%)7?%!(>%!%@%?7(6/'!(64%&!/B!*/(>!/B!(>/&%!5$/?%)7$%&!(/!8K!A%!4-+!&(6GG!*%!B-?%)!A6(>!-!5$/;$-4!(>-(!6&!N(//!&G/ALQ!!

MB7 SH"2&%3&%#32,6/+#2"2%.#&"#D&KH4&%3&%2&%/5.,2"#2T&

W'&($74%'(-(6/'!6&!(>%!A-+!+/7!4-J%!(>%!5%$B/$4-'?%!/B!+/7$!-55G6?-(6/'&!%-&+!(/!4/'6(/$!-')!$%V6%AL!W'!;%'%$-GK!(>%!57$5/&%!/B!6'&($74%'(-(6/'!6&!(/H!

• .%-&7$%!(>%!-(($6*7(%&!/B!-!&+&(%4!

• C$%-(%!-!4/'6(/$6';!4%?>-'6&4!

• :$-?J!-55G6?-(6/'!5%$B/$4-'?%!

#$/5%$!6'&($74%'(-(6/'!-GG/A&!+/7!(/!?/GG%?(!5$/5%$G+!&?/5%)!(646';!-')!($-?%!6'B/$4-(6/'!B/$!(>%!(-&J&!(>-(!-$%!645/$(-'(!(/!+/7$!*7&6'%&&L!W'&($74%'(-(6/'!&>/7G)!*%!G6;>(A%6;>(!-')!%-&+!(/!-?(6V-(%!-')!7G(64-(%G+!4-J%!(>%!*7&6'%&&!/B!B6')6';!-')!B6@6';!5%$B/$4-'?%!6&&7%&!X76?J!-')!&645G%L!

! M6(>!6'&($74%'(-(6/'K!+/7!?-'!%-&6G+!-'&A%$!X7%&(6/'&!G6J%H!

• M>+!)6)!(>6&!(-J%!&/!G/';R!

• M>-(!A/7G)!>-55%'!6BR!

• W&!(>6&!(>6';!%BB6?6%'(R!

• I4!W!)/'%!+%(R!

Y%6';!-*G%!(/!-'&A%$!(>%&%!X7%&(6/'&!A6GG!-GG/A!+/7!(/!5$/V6)%!(>%!B6'-G!A/$)!/'!)6&57(%&!*%(A%%'!<YI&K!)%V%G/5%$&K!4-'-;%$&K!-')!7&%$&L!Z/7!J'/A!A>%$%!+/7$!-55G6?-(6/'!&5%')&!6(&!(64%!-')!+/7!?-'!B/?7&!/'!B6@6';!(>%!&5%?6B6?!-$%-&!A>%$%!(//!47?>!(64%!6&!G/&(L!

MBM 1.K&D.&4.6&"DD&%#32,6/+#2"2%.#T&

F$-?G%!&755G6%&!*76G(^6'!5-?J-;%&!(>-(!?-'!*%!7&%)!-&!(>%!*76G)6';!*G/?J&!B/$!6'&($74%'(6';!+/7$!?/)%L!:>%$%!-$%!(>$%%!5$64-$+!5-?J-;%&!(>-(!?-'!>-')G%!-G4/&(!%V%$+!%G%4%'(!'%%)%)!(/!)/!(>%!]/*!5$/5%$G+H!

• <Y.3j3E33WFi!

• <Y.3j3Z3:E.!

• <Y.3jI##PWCI:WFijWikF!

MBMB9 ;U!'V'F''<?=&

:>6&!5-?J-;%!6&!7&%)!(/!A$-5!-??%&&!(/!IP:ES!3E33WFi!-')!3E:!SFPE!&(-(%4%'(&!-&!A%GG!-&!/(>%$!&%&&6/'!6'B/$4-(6/'L!M>6G%!(>%$%!-$%!'74%$/7&!&7*5$/;$-4&!-V-6G-*G%K!(>%!3E:jW<Ei:WkWES!5$/?%)7$%!6&!/B!5$64-$+!7&%!A>%'!?$%-(6';!6'&($74%'(-(6/'!B/$!+/7$!?/)%L!

! T&6';!3E:jW<Ei:WkESK!+/7!-$%!-*G%!(/!%&(-*G6&>!(>%!?7$$%'(!&%&&6/'!A6(>!-!?G6%'(!6)%'(6B6%$L!:>-(!6)%'(6B6%$!A6GG!(>%'!*%!-&&/?6-(%)!A6(>!(>-(!&%&&6/'!7'(6G!6(!)6&?/''%?(&!/$!?G%-$&!(>%!6)%'(6B6%$L!:>%!?G6%'(!6)%'(6B6%$!+/7!?>/&%!?-'!*%!75!(/!D\!?>-$-?(%$&!6'!G%';(>L!FV%$!\8!)+'-46?!V6%A&K!&7?>!-&!gh3E33WFiK!?/'(-6'!-!?G6%'(j6)%'(6B6%$!/$!?G6%'(j6)!?/G74'L!F'?%!+/7!&%(!(>6&!V-G7%K!6(!A6GG!*%!7&%)!(/!5/57G-(%!(>%&%!V6%A!?/G74'&!(>7&!-GG/A6';!+/7!(/!X7%$+!(>%4!(/!G/?-(%!&%&&6/'!6'B/$4-(6/'!&5%?6B6?!(/!(>-(!6)%'(6B6%$L!

Page 17: Managing SQL Performance

! ! ! ! !! !

! ! ! ! "c!

! :>%!?-GG!(/!3E:jW<Ei:WkWES!?-'!*%!4-)%!-(!-'+!5/6'(!6'!(64%K!*7(!-!5$-?(6?-G!/5(6/'!46;>(!*%!(/!>-V%!(>%!6)%'(6B6%$!&%(!V6-!-!G/;/'!($6;;%$L!Z/7!?-'!-??%&&!6'B/$4-(6/'!-*/7(!(>%!&%&&6/'!-G$%-)+!-V-6G-*G%!B$/4!3Z3jCFi:Es:!-')!5/57G-(%!(>%!?G6%'(j6)%'(6B6%$!644%)6-(%G+!75/'!G/;/'L!:>-(!A-+K!(>%!&%&&6/'!6&!6)%'(6B6%)!644%)6-(%G+L!k/$!%@-45G%K!+/7!?/7G)!?$%-(%!-!G/;/'!($6;;%$!&/4%(>6';!G6J%!(>6&H!

CREATE OR REPLACE TRIGGER client_id_logon_trg AFTER LOGON ON DATABASE DECLARE my_service SYS.V_$SESSION.SERVICE_NAME%TYPE; my_clientid SYS.V_$SESSION.CLIENT_IDENTIFIER%TYPE; my_ip_address SYS.V_$SESSION.TERMINAL%TYPE; my_os_user SYS.V_$SESSION.OSUSER%TYPE; my_audsid SYS.V_$SESSION.AUDSID%TYPE; my_program SYS.V_$SESSION.PROGRAM%TYPE; CLIENT_ID_DELIM CHAR(1) := '~'; BEGIN IF USER NOT IN ('SYS') AND USER IS NOT NULL THEN my_clientid := SYS_CONTEXT('USERENV', 'CLIENT_IDENTIFIER'); IF my_clientid IS NULL THEN my_service := SYS_CONTEXT('USERENV', 'SERVICE_NAME'); my_ip_address := NVL(SYS_CONTEXT('USERENV', 'IP_ADDRESS'),SYS_CONTEXT('USERENV', 'TERMINAL')); my_os_user := SYS_CONTEXT('USERENV', 'OS_USER'); my_audsid := TO_NUMBER(SYS_CONTEXT('USERENV', 'SESSIONID')); SELECT PROGRAM INTO my_program FROM SYS.V_$SESSION WHERE AUDSID = my_audsid AND ROWNUM = 1; DBMS_SESSION.SET_IDENTIFIER(my_os_user || CLIENT_ID_DELIM || my_ip_address || CLIENT_ID_DELIM || my_program || CLIENT_ID_DELIM || my_service); END IF; END IF; EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('client_id_logon_trg: Exception thrown'); END client_id_logon_trg;

MBMB7 ;U!'V'W':F!&

:>6&!5-?J-;%!?/'(-6'&!-!'74*%$!/B!5$/?%)7$%&!(>-(!?-'!*%!7&%B7G!5-$(6?7G-$G+!B/$!%46((6';!4%-'6';B7G!6'B/$4-(6/'!(/!($-?%!B6G%&!-')!-G%$(!G/;&L!F$-?G%!)/%&'U(!B/$4-GG+!)/?74%'(!(>6&!5-?J-;%!/$!%@5G6?6(G+!&755/$(!6(!&/!5$/?%%)!-(!+/7$!/A'!$6&J!A>%'!7&6';!6(L!

! :>%!(A/!4-6'!5$/?%)7$%&!(/!>%G5!6'&($74%'(!+/7$!?/)%!-$%!,3<<<:!-')!,3<MS:L!,3<<<:!?-'!*%!?-GG%)!(/!%46(!-!)-(%=(64%!&(-45!6'(/!-!($-?%!B6G%L!:>6&!?-'!*%!>%G5B7G!B/$!%@5G6?6(G+!6)%'(6B+6';!(>%!(64%!-(!A>6?>!?%$(-6'!-?(6/'&!+/7!-$%!4/'6(/$6';!*%;6'!-')!%')L!:>%!,3<MS:!5$/?%)7$%!-G&/!A$6(%&!(/!-!($-?%!B6G%K!/$!(/!(>%!-G%$(!G/;!-&!A%GGK!*7(!/BB%$&!(>%!-*6G6(+!(/!A$6(%!/7(!-'+!4%&&-;%!+/7!?>//&%L!I;-6'K!(>6&!?-'!>%G5!+/7!5G-?%!6'B/$4-(6/'!6'(/!($-?%!B6G%&!(>-(!4-J%&!(>%4!%-&6%$!(/!&?/5%!(/!&5%?6B6?!(-&J&!-&!(>%+!/??7$L!

MBMBM ;U!'VC**)<AC:<?=V<=G?&

:>%!<Y.3jI##PWCI:WFijWikF!5-?J-;%!6&!(>%!%&&%'(6-G!%G%4%'(!7&%)!(/!-&&6&(!?/)%!6'&($74%'(-(6/'L!:>6&!5-?J-;%!-GG/A&!+/7!(/!6)%'(6B+!&5%?6B6?!&%?(6/'&!/B!+/7$!?/)%!*+!4-$J6';!(>%4!A6(>!7'6X7%G+!6)%'(6B+6';!4/)7G%!-')!-?(6/'!'-4%&L!:>%&%!4/)7G%!-')!-?(6/'!V-G7%&!A6GG!*%!$%?/$)%)!-')!4-)%!V6&6*G%!(>$/7;>!'74%$/7&!gh!V6%A&!(/!>%G5!+/7!6)%'(6B+!-?(6V6(6%&!-&&6;'%)!(/!-')!$%&/7$?%&!7&%)!*+!(>%!?/)%!%@%?7(%)!A6(>6'!(>%&%!4/)7G%=-?(6/'!5-6$!6)%'(6B6?-(6/'&L!i/(!/'G+!-$%!(>%!4/)7G%=-?(6/'!5-6$&!7&%)!(/!($-?J!-?(6V6(6%&!*7(!(>%+!?-'!*%!7&%)!(/!>%G5!(7$'!($-?6';!/'!-')!/BB!-&!A%GGL!

! :>%!3E:j.F<TPE!-')!3E:jIC:WFi!5$/?%)7$%&!-$%!7&%)!(/!&%(!(>%!'-4%!/B!(>%!?7$$%'(!-55G6?-(6/'!/$!4/)7G%L!:>%!4-6'!)6BB%$%'?%!*%(A%%'!(>%!(A/!6&!(>-(!3E:j.F<TPE!-GG/A&!+/7!(/!&%(!*/(>!-!4/)7G%j'-4%!

Page 18: Managing SQL Performance

! ! !

!

! ! ! ! ! ! ! ! ! ! !"e!

-')!-?(6/'j'-4%!6'!-!&6';G%!?-GGK!A>%$%-&!3E:jIC:WFi!/'G+!&%(&!(>%!-?(6/'j'-4%L!T&6';!3E:j.F<TPEK!+/7!?-'!%&(-*G6&>!(>%!&(-$(!/B!-!*7&6'%&&!(-&J!-')!-!5-$(6?7G-$!-?(6/'!A6(>6'!(>-(!(-&J!-&!B/GG/A&H!

DBMS_APPLICATION_INFO. set_module( module_name=>'Order Entry', action_name=>'Get Order Items')

:>%!4/)7G%!6&!6)%'(6B6%)!-&!tF$)%$!E'($+U!-')!(>%!-?(6/'!6&!td%(!F$)%$!W(%4&UL!:>%!tF$)%$!E'($+U!4/)7G%!?/7G)!?/'(-6'!47G(65G%!-?(6/'&!6'!-))6(6/'!(/!(>%!td%(!F$)%$!W(%4&U!&7?>!-&!t<%G%(%!F$)%$!W(%4&U!/$!tT5)-(%!F$)%$!W(%4&UL!!

:>%!?-GG&!(/!3E:j.F<TPE!/$!3E:jIC:WFi!&>/7G)!*%!5G-?%)!-(!(>%!&(-$(!/B!(>%!5-$(6?7G-$!*7&6'%&&!(-&J!-')!*%!$%&%(!_6L%L!&%(!(/!'7GG`!A>%'!(>-(!(-&J!6&!?/45G%(%!/$!A>%'%V%$!-'!%$$/$!/??7$&L!W'!/$)%$!(/!>%G5!6)%'(6B+!(>%!?7$$%'(!4/)7G%!-')!-?(6/'!(>-(!6&!6'!5G-?%K!(>%!SEI<j.F<TPE!5$/?%)7$%!6&!7&%)L!M>%'%V%$!+/7!$%&%(!(>%!4/)7G%!-')!-?(6/'!-(!(>%!*%;6''6';!/B!-!(-&JK!+/7!&>/7G)!4-J%!&7$%!(/!?>%?J!6B!-!4/)7G%=-?(6/'!6&!-G$%-)+!6'!5G-?%L!WB!&/K!+/7!A/7G)!&(/$%!(>-(!6'B/$4-(6/'!-')!(>%'!$%&%(!4/)7G%!-')!-?(6/'!(/!(>/&%!V-G7%&!A>%'!(>%!?7$$%'(!(-&J!?/45G%(%&L!:>%!B/GG/A6';!G6&(6';!5$/V6)%&!-!&>/$(!%@-45G%H!

PROCEDURE get_emp_simple_instr IS fnlist_stack fnlist_tab; lnlist_stack lnlist_tab; BEGIN DBMS_APPLICATION_INFO.set_module(module_name => 'Human Resources' ,action_name => 'Get Employees'); SELECT first_name, last_name BULK COLLECT INTO fnlist_stack, lnlist_stack FROM employees; DBMS_APPLICATION_INFO.set_module(NULL, NULL); EXCEPTION WHEN OTHERS THEN DBMS_APPLICATION_INFO.set_module(NULL, NULL); DBMS_OUTPUT.PUT_LINE('HR_Package.get_emp_simple_instr => ERROR'); END get_emp_simple_instr;

:>6&!%@-45G%!A/$J&!]7&(!B6'%!-')!A6GG!&%(!(>%!4/)7G%!-')!-?(6/'!-(!(>%!*%;6''6';!/B!(>%!?/)%!*/)+!-')!$%&%(!6(!-(!(>%!%')K!6B!&7??%&&B7GK!/$!A6(>6'!(>%!%@?%5(6/'!>-')G%$!6B!-'!%$$/$!/??7$&L!Y7(K!A>-(!A/7G)!>-55%'!6B!(>6&!5$/?%)7$%!A-&!?-GG%)!6'&6)%!-'/(>%$!5$/?%)7$%!(>-(!A-&!&646G-$G+!6'&($74%'(%)!-&!B/GG/A&R!

PROCEDURE get_emp_jobs_instr_flawed IS jtlist_stack jtlist_tab; lnlist_stack lnlist_tab; BEGIN DBMS_APPLICATION_INFO.set_module(module_name => 'Human Resources', action_name => 'Get Employees and Jobs'); get_emp_simple_instr; SELECT last_name, job_title BULK COLLECT INTO lnlist_stack, jtlist_stack FROM employees e, jobs j WHERE e.job_id = j.job_id; DBMS_APPLICATION_INFO.set_module(NULL, NULL); EXCEPTION WHEN OTHERS THEN DBMS_APPLICATION_INFO.set_module(NULL, NULL); DBMS_OUTPUT.PUT_LINE('get_emp_jobs_instr_flawed => ERROR'); END get_emp_jobs_instr_flawed;

i/(%!(>-(!-B(%$!(>%!4/)7G%!-')!-?(6/'!-$%!&%(!6'!(>6&!5$/?%)7$%K!/7$!/(>%$!5$/?%)7$%!6&!?-GG%)L!M>-(U&!(>%!5$/*G%4R!:>%!5$/*G%4!6&!(>-(!644%)6-(%G+!-B(%$!(>%!4/)7G%!-')!-?(6/'!-$%!&%(!B/$!(>6&!5$/?%)7$%K!(>%!;%(j%45j&645G%j6'&($!5$/?%)7$%!6&!?-GG%)!-')!6'&6)%!(>-(!5$/?%)7$%K!(>%!4/)7G%!-')!-?(6/'!-$%!&%(!(/!V-G7%&!&5%?6B6?!(/!(>-(!5$/?%)7$%L!I(!(>%!%')!/B!(>-(!?-GGK!(>%!4/)7G%!-')!-?(6/'!-$%!&%(!(/!'7GGL!:>%$%B/$%K!A>%'!(>%!3EPEC:!&(-(%4%'(!6&!%@%?7(%)!-B(%$!(>-(!5$/?%)7$%!?-GG!?/45G%(%&K!/7$!?7$$%'(!4/)7G%!-')!-?(6/'!>-V%!*%%'!G/&(L!:>6&!6&!A>%$%!A%!'%%)!(/!4-J%!&7$%!?-5(7$%!(>%!4/)7G%!-')!-?(6/'!V-G7%&!-')!$%(-6'!(>%4!&/!(>-(!A%!?-'!

Page 19: Managing SQL Performance

! ! ! ! !! !

! ! ! ! "m!

-GA-+&!$%&%(!(>%4!5$/5%$G+!A>%'!?-GG&!-$%!4-)%!(/!/(>%$!6'&($74%'(%)!5$/?%)7$%&L!:>%!5$/5%$!A-+!(/!?/)%!(>%!/$6;6'-G!5$/?%)7$%!A/7G)!*%!G6J%!(>6&H!

PROCEDURE get_emp_instr_good IS fnlist_stack fnlist_tab; lnlist_stack lnlist_tab; preModuleName VARCHAR2(48) := NULL; preActionName VARCHAR2(32) := NULL; BEGIN DBMS_APPLICATION_INFO.read_module( module_name => preModuleName, action_name => preActionName); DBMS_APPLICATION_INFO.set_module( module_name => 'Human Resources', action_name => 'Get Employees'); SELECT first_name, last_name BULK COLLECT INTO fnlist_stack, lnlist_stack FROM employees; DBMS_APPLICATION_INFO.set_module( module_name => preModuleName, action_name => preActionName); EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('HR_Package.get_emp_instr_good ERROR'); DBMS_APPLICATION_INFO.set_module( module_name => preModuleName, action_name => preActionName); END get_emp_instr_good;

i/A!(>%!?-GG6';!5$/?%)7$%U&!4/)7G%!-')!-?(6/'!A6GG!*%!5$%&%$V%)!5$/5%$G+L!!

MBX !"R%#$&%#32,6/+#2"2%.#&+"34&

T&6';!(>%!&755G6%)!5-?J-;%&!6&!$%G-(6V%G+!&($-6;>(^B/$A-$)!*7(!)/%&!$%X76$%!&/4%!4-'-;%4%'(!/B!(>%!?-GG!&(-?J!&/!(>-(!+/7!4-6'(-6'!(>%!5$/5%$!4/)7G%=-?(6/'!&%((6';&!-&!+/7!?>-';%!(-&J&L!k/$(7'-(%G+K!(>%$%!6&!-!B$%%K!F5%'!3/7$?%!5-?J-;%!?-GG%)!(>%!W'&($74%'(-(6/'!P6*$-$+!B/$!F$-?G%!_WPF`!-V-6G-*G%!-(!3/7$?%k/$;%!_4))56773(@#.$>(#+$&%$)75#(8$.)37,-(`!(>-(!>-&!*%%'!A$6((%'!-')!4-6'(-6'%)!*+!.%(>/)!S!C/$5/$-(6/'!(>-(!%'?-5&7G-(%&!(>%!7&%!/B!(>%!5-?J-;%&!A%UV%!]7&(!)6&?7&&%)!-')!4-J%&!6(!)%-)!&645G%!(/!645G%4%'(L!

:>%!B/GG/A6';!&%?(6/'!6&!?/56%)!B$/4!(>%!.%(>/)!S!A%*&6(%!-(!4))5677'$)4(1B#&.('73(>)2"#$7,-(L!

WPFK!(>%!6'&($74%'(-(6/'!G6*$-$+!B/$!F$-?G%K!6&!-'!/5%'!&/7$?%!5$/]%?(!)6$%?(%)!*+!.%(>/)!S!C/$5/$-(6/'!)%V%G/5%$&L!W(!?/'(-6'&!#P=3OP!5$/?%)7$%&!B/$!4%-&7$6';!(-&J&!7&6';!F$-?G%!%@(%')%)!3OP!($-?%L!:>%!5$/?%)7$%&!-$%!6'&(-GG%)!-')!?/456G%)!6'!(>%!(-$;%(!F$-?G%!6'&(-'?%!-')!?-'!*%!?-GG%)!*+!?/)%!A$6((%'!6'!-'+!G-';7-;%!(>-(!>-&!-??%&&!(/!(>%!F$-?G%!)-(-*-&%!(6%$L!WPF!6&!B7GG+!6'(%;$-(%)!A6(>!(>%!.%(>/)!S!3PI!.-'-;%$K!A>6?>!-GG/A&!+/7!(/!/$;-'6[%!-')!-'-G+[%!$%&5/'&%!(64%!)-(-!B/$!+/7$!%'(6$%!-55G6?-(6/'!&+&(%4L!!

MBXB9 <#32,6/+#2"2%.#&)%P,",4&-.,&?,"0N+&Y<)?Z&

<6-;'/&6';!-')!$%5-6$6';!5%$B/$4-'?%!5$/*G%4&!6'!-'!F$-?G%!%'V6$/'4%'(!?-'!*%!-!?/45G6?-(%)!-')!(64%^?/'&746';!]/*L!2/A%V%$K!+/7K!(>%!I55G6?-(6/'!<%V%G/5%$K!?-'!4-J%!(>%!]/*!47?>!&645G%$!*+!6'&%$(6';!-!B%A!%@($-!G6'%&!/B!?/)%K!?-GG%)!6'&($74%'(-(6/'K!6'(/!+/7$!-55G6?-(6/'&L!M6(>!(>%!$6;>(!6'&($74%'(-(6/'!G6*$-$+K!(>%!]/*!6&!%-&+L!:>%!W'&($74%'(-(6/'!P6*$-$+!B/$!F$-?G%!_WPF`!;6V%&!+/7!(>%!G6'%&!/B!?/)%!+/7!'%%)L!

MBXB7 :H+&*"4.--&

W'&($74%'(-(6/'!4-J%&!+/7$!?/)%!B-&(%$K!%-&6%$!(/!4-6'(-6'K!-')!?>%-5%$!(/!A$6(%L!W'&($74%'(-(6/'!4-J%&!+/7$!?/)%!B-&(%$!*%?-7&%!6(!&>/A&!+/7!-GG!/B!+/7$!/55/$(7'6(6%&!B/$!4-J6';!+/7$!?/)%!4/$%!%BB6?6%'(K!$6;>(!A>6G%!+/7u$%!A$6(6';!6(L!W'&($74%'(-(6/'!4-J%&!+/7$!?/)%!%-&6%$!(/!4-6'(-6'!*%?-7&%!6(!&>/A&!+/7!%@-?(G+!A>-(!+/7$!-'-G+&(&!-$%!(-GJ6';!-*/7(!A>%'!(>%+!&-+!(>-(!+/7$!?/)%!6&!(//!&G/AL!W'&($74%'(-(6/'!4-J%&!+/7$!?/)%!?>%-5%$!(/!A$6(%!*%?-7&%!6(!B/?7&%&!+/7$!(7'6';!%BB/$(&!/'G+!75/'!(>%!5-$(&!/B!+/7$!?/)%!A>%$%!5%$B/$4-'?%!$%-GG+!4-((%$&L!d//)!6'&($74%'(-(6/'!6&!+/7$!6447'6[-(6/'!-;-6'&(!(>%!&6'&!/B!5$%4-(7$%!/5(646[-(6/'L!

Page 20: Managing SQL Performance

! ! !

!

! ! ! ! ! ! ! ! ! ! !08!

MBXBM 1.K&<2&S.,R3&

WPF!6&!&75%$!%-&+!B/$!&/B(A-$%!)%V%G/5%$&!(/!7&%L!Z/7!4-$J!(>%!*%;6''6';!-')!%')!/B!+/7$!(-&J&!A6(>!-!&6';G%!G6'%!/B!?/)%K!;6V6';!%-?>!(-&J!-!4/)7G%!-')!-?(6/'L!:>6'J!/B!(>%!-?(6/'!-&!(>%!'-4%!/B!(>%!(-&J!6(&%GBK!-')!(>%!4/)7G%!-&!(>%!5-$(!/B!(>%!-55G6?-(6/'!(>-(!(>%!(-&J!$%5$%&%'(&L!

.-$J6';!(>%!*%;6''6';!/B!-'+!(-&J!6&!-&!&645G%!-&!4-J6';!-!5$/?%)7$%!?-GG!-')!6'?G7)6';!(>%!V-G7%&!B/$!

.F<TPEK!IC:WFiK!-')!CF..Ei:L!

M>%'!4-$J6';!(>%!%')!/B!-!(-&JK!+/7!-;-6'!4-J%!-!&645G%!5$/?%)7$%!?-GGL!Y%!&7$%!(/!6'?G7)%!-GG!5/&&6*G%!%@6(!5/6'(&!/B!(>%!4-$J%)!(-&J!6'?G7)6';!-'+!EsCE#:WFi!&%?(6/'&!6'!+/7$!?/)%L!

BEGIN ilo_task.begin_task(module => 'Load Transaction Tables', action => 'Begin overall load', comment => 'Execution of procedure all_trx_table_loads'); // ... code to perform task goes here ilo_task.end_task; EXEPTION WHEN ex_insert_problem THEN ilo_task.end_task(error_num => SQLCODE); WHEN ex_update_problem THEN ilo_task.end_task(error_num => SQLCODE); WHEN others THEN ilo_task.end_task(error_num => SQLCODE); END;

MBXBX )%0+#3%#$&

WPF!6&!/5%'!&/7$?%!&/B(A-$%!-V-6G-*G%!-(!3/7$?%k/$;%L!W(!6&!$%G%-&%)!B$%%!/B!?>-$;%!7')%$!(>%!diT!P%&&%$!d%'%$-G!#7*G6?!P6?%'&%!_Pd#P`L!

E')!/B!?/56%)!6'B/$4-(6/'L!

MB[ &CDD%#$&.#&2.&2H+&<)?&

F'%!/B!(>%!?//G%&(!(>6';&!-*/7(!(>%!WPF!6&!(>-(!+/7!?-'!4/)6B+!6(!(/!6'?G7)%!-'+!-))6(6/'-G!6'&($74%'(-(6/'!B%-(7$%&!+/7!4-+!A-'(L!k/$!6'&(-'?%K!6'!?>-5(%$!"1!/B!E@5%$(!F$-?G%!#$-?(6?%&H!F$-?G%!<-(-*-&%!I)46'6&($-(6/'!B$/4!(>%!F-J!:-*G%K!-')!?>-5(%$!"a!/B!#$/!F$-?G%!3OP!_*/(>!B$/4!I5$%&&`K!S/*+'!3-')&!6'?G7)%&!&/4%!;$%-(!6'B/$4-(6/'!/'!>/A!&>%!>-&!%@(%')%)!(>%!WPF!(/!)/!4/$%!(>-'!]7&(!4-$J!>%$!?/45-'+U&!?/)%!A6(>!4/)7G%!-')!-?(6/'!'-4%&L!

C>-5(%$!"a!>-&!*%%'!$%5$6'(%)!-')!6'?G7)%)!-(!(>%!%')!/B!(>6&!)/?74%'(!B/$!+/7$!$%B%$%'?%L!

! WUV%!7&%)!WPF!B/$!X76(%!-!A>6G%!-')!>-V%!645G%4%'(%)!V-$6/7&!-))^/'&!B/$!4+&%GB!-')!B/$!?G6%'(&!*7(!'%V%$!B/$4-G6[%)!-'+(>6';!$%7&-*G%L!S/*+'!>-&!)/'%!]7&(!(>-(!A6(>!>%$!4/)6B6?-(6/'&!(/!WPF!-')!>-&!4-)%!(>/&%!75)-(%&!-V-6G-*G%!-&!5-$(!/B!(>%!&/7$?%!?/)%!)/A'G/-)&!B/$!(>%!(A/!*//J&L!W!*%G6%V%!(>%!4/)6B6?-(6/'&!-$%!;/6';!(/!%V%'(7-GG+!6'?/$5/$-(%)!6'(/!(>%!*-&%!WPF!5-?J-;%K!*7(!7'(6G!(>%+!-$%K!+/7!?-'!;%(!(>%4!B$/4!(>%!I5$%&&!*//J!&/7$?%!?/)%!5-;%&L0!

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!0!4))5677"5#$33&.('70((!71(2%-("1>,-$7E<9E!_E@5%$(!#$-?(6?%&`!-')!4))5677"5#$33&.('70((!71(2%-("1>,-$7EF<;/_#$/!F$-?G%!3OP`L!!

Page 21: Managing SQL Performance

! ! ! ! !! !

! ! ! ! 0"!

X '()&C=:<*C::F>='&

I'!-'(65-((%$'!6&!-!(%?>'6X7%K!/$!5-((%$'K!(>-(!6&!?/44/'G+!7&%)!(/!&/GV%!-!5-$(6?7G-$!5$/*G%4!*7(!/B(%'!G%-)&!(/!/(>%$!5$/*G%4&L!37?>!(%?>'6X7%&!-$%!(>/7;>(!(/!*%!*%'%B6?6-GK!*7(!(>%+!7G(64-(%G+!5$/)7?%!4/$%!6GG!%BB%?(&!(>-'!5/&6(6V%!$%&7G(&L!:>%!)6BB%$%'?%!*%(A%%'!-'!-'(65-((%$'!-')!-!46&(-J%!/$!*-)!5$-?(6?%!6&!(>-(!-'!-'(65-((%$'!>-&!-!$%B-?(/$%)!&/G7(6/'!(>-(!?-'!*%!)/?74%'(%)!-')!5$/V%'!(/!5$/V6)%!(>%!)%&6$%)!$%&7G(!4/$%!%BB%?(6V%G+L!

! :>%!3OP!-'(65-((%$'&!(>-(!B/GG/A!-$%!&/4%!/B!(>%!4/&(!B$%X7%'(G+!4-)%!46&&(%5&!WUV%!&%%'!4-)%L!./&(!/B!(>%4!W!4-)%!4+&%GB!-(!/'%!(64%!/$!-'/(>%$!-')!(>%$%U&!'/!*%((%$!A-+!(/!G%-$'!>/A!%()!(/!)/!&/4%(>6';!(>-'!(/!&5%')!4-'+!>/7$&!4-J6';!75!B/$!/'%U&!/A'!%$$/$&L!!

XB9 ;%I%D+&"#D&0.#\6+,&

WUV%!&%%'!(>6&!B6$&(!-'(65-((%$'!/V%$!-')!/V%$!(>$/7;>!(>%!+%-$&L!WU4!/B(%'!;G-)!(/!&%%!6(!>/A%V%$!-&!6(!;6V%&!4%!-'!%-&+!A6'!A6(>!-!?G6%'(!A>/!>-&!*%?/4%!B$7&($-(%)!A6(>!&/4%!5//$G+!5%$B/$46';!56%?%!/B!3OP!-')!6&!B%%G6';!-(!-!G/&&!B/$!%V%$!B6')6';!-!A-+!(/!645$/V%!6(L!W!?-GG!(>6&!/'%!(>%!N)6V6)%!-')!?/'X7%$Q!-'(65-((%$'!*%?-7&%!(>%!&/G7(6/'!$%X76$%&!(>-(!+/7!&5G6(!(>%!3OP!&(-(%4%'(!6'(/!?>7'J&!6'!/$)%$!(/!/5(646[%!6(L!:>6&!4-+!?$%-(%!4-J%!(>%!3OP!G/';%$K!*7(!6(!A6GG!-G&/!*%!B-&(%$!-')!$%X76$%!G%&&!$%&/7$?%&!(/!%@%?7(%L!

! M>%'%V%$!-!&6';G%!3OP!&(-(%4%'(!?-'!*%!7&%)!(/!5$/)7?%!-!$%&7G(K!6(!6&!(>/7;>(!(>6&!6&!5$%B%$-*G%!(/!)/6';!47G(65G%!&(-(%4%'(&!-')!?%$(-6'G+!6&!*%((%$!(>-'!)/6';!$/A^*+^$/A!5$/?%&&6';L!Y7(K!($+6';!(/!)/!%V%$+(>6';!+/7!'%%)!(/!)/!6'!-!&6';G%K!?/45G%@!&(-(%4%'(!?-'!-G&/!*%!(>%!$//(!?-7&%!/B!-'/(>%$!5$/*G%4H!7&6';!(//!4-'+!$%&/7$?%&!(/!5$/)7?%!(>%!$%&7G(!'%%)%)L!

XB9B9 FQ"/5N+&9]&<#D+Q&G6NN&'0"#&!%#^!"Q&?52%/%O"2%.#&

! WUV%!&%%'!(>6&!B6$&(!%@-45G%!)%4/'&($-(%)!-!47G(6(7)%!/B!(64%&K!-')!B/$!(>6&!/'%!&5%?6B6?!%@-45G%K!W!(>6'J!4/&(!5%/5G%!>-V%!G%-$'%)!'/(!(/!)/!(>6&L!Y7(K!6(!6&!-'!%-&+!A-+!(/!&>/A!>/A!*$%-J6';!3OP!-5-$(!?-'!>-V%!-4-[6';!645-?(L!!

! M>%'!%@%?7(6';!-'!-;;$%;-(%!X7%$+!(>-(!'%%)&!(/!$%(7$'!*/(>!(>%!46'6474!-')!4-@6474!V-G7%!B/$!-!?/G74'K!(>%!?/44/'!?>/6?%!6&!(/!%@%?7(%!-!&6';G%!X7%$+!A6(>!(>%!46'!-')!4-@!$%(7$'%)!-&!B/GG/A&H!

SQL> select /* km_min */ min(department_id) from employees ; MIN(DEPARTMENT_ID) ------------------ 10 SQL> @pln km_min SQL_ID 3xfdnnga1pcdf, child number 0 ------------------------------------- Plan hash value: 613773769 ---------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | ---------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 1 | | 1 | SORT AGGREGATE | | 1 | 1 | 1 |00:00:00.01 | 1 | | 2 | INDEX FULL SCAN (MIN/MAX)| EMP_DEPARTMENT_IX | 1 | 1 | 1 |00:00:00.01 | 1 | ---------------------------------------------------------------------------------------------------------- SQL> SQL> select /* km_max */ max(department_id) from employees ; MAX(DEPARTMENT_ID) ------------------ 110 SQL> @pln km_max

Page 22: Managing SQL Performance

! ! !

!

! ! ! ! ! ! ! ! ! ! !00!

SQL_ID 9j878bkhc57aj, child number 0 ------------------------------------- Plan hash value: 613773769 ---------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | ---------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 1 | | 1 | SORT AGGREGATE | | 1 | 1 | 1 |00:00:00.01 | 1 | | 2 | INDEX FULL SCAN (MIN/MAX)| EMP_DEPARTMENT_IX | 1 | 1 | 1 |00:00:00.01 | 1 | ---------------------------------------------------------------------------------------------------------- SQL> SQL> select /* km_min_max */ min(department_id), max(department_id) from employees ; MIN(DEPARTMENT_ID) MAX(DEPARTMENT_ID) ------------------ ------------------ 10 110 SQL> @pln km_min_max SQL_ID 18883amg21pnp, child number 0 ------------------------------------- Plan hash value: 1756381138 ------------------------------------------------------------------------------------------ | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | ------------------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 7 | | 1 | SORT AGGREGATE | | 1 | 1 | 1 |00:00:00.01 | 7 | | 2 | TABLE ACCESS FULL| EMPLOYEES | 1 | 107 | 107 |00:00:00.01 | 7 | ------------------------------------------------------------------------------------------ SQL> select /* km_split */ 2 (select min(department_id) from employees) min_id, 3 (select max(department_id) from employees) max_id 4 from dual ; MIN_ID MAX_ID --------------- --------------- 10 110 SQL> @pln km_split SQL_ID ad2bpg2z204b5, child number 0 ------------------------------------- Plan hash value: 2189307159 ---------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | ---------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 0 | | 1 | SORT AGGREGATE | | 1 | 1 | 1 |00:00:00.01 | 1 | | 2 | INDEX FULL SCAN (MIN/MAX)| EMP_DEPARTMENT_IX | 1 | 1 | 1 |00:00:00.01 | 1 | | 3 | SORT AGGREGATE | | 1 | 1 | 1 |00:00:00.01 | 1 | | 4 | INDEX FULL SCAN (MIN/MAX)| EMP_DEPARTMENT_IX | 1 | 1 | 1 |00:00:00.01 | 1 | | 5 | FAST DUAL | | 1 | 1 | 1 |00:00:00.01 | 0 | ----------------------------------------------------------------------------------------------------------

! W'!(>6&!?-&%K!(>%!/7(57(!A-&!)%&6$%)!-&!-!&6';G%!$/AK!&/!(>%!(A/!X7%$6%&!-$%!&645G+!A$6((%'!-&!(A/!&6';G%^?/G74'!&7*^X7%$6%&L!T&6';!-!TiWFi!/B!(>%!(A/!X7%$6%&!A/7G)!>-V%!*%%'!-'/(>%$!-G(%$'-(6V%K!6B!(>%!/7(57(!A-&'U(!)%&6$%)!-&!-!&6';G%!$/AL!

SQL> select /* km_union */ 2 min(department_id) id 3 from employees 4 union 5 select max(department_id) 6 from employees; ID --------------- 10 110 SQL> @pln km_union

Page 23: Managing SQL Performance

! ! ! ! !! !

! ! ! ! 01!

SQL_ID cvgw7gu0r4w62, child number 0 ------------------------------------- Plan hash value: 1280351157 --------------------------------------------------------------------------------------------------------------------------------- | Id | Operation | Name |Starts |E-Rows |A-Rows | A-Time |Buffers | OMem | 1Mem | Used-Mem | --------------------------------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | | 2 |00:00:00.01 | 2 | | | | | 1 | SORT UNIQUE | | 1 | 2 | 2 |00:00:00.01 | 2 | 2048 | 2048 | 2048 (0)| | 2 | UNION-ALL | | 1 | | 2 |00:00:00.01 | 2 | | | | | 3 | SORT AGGREGATE | | 1 | 1 | 1 |00:00:00.01 | 1 | | | | | 4 | INDEX FULL SCAN (MIN/MAX)| EMP_DEPARTMENT_IX | 1 | 1 | 1 |00:00:00.01 | 1 | | | | | 5 | SORT AGGREGATE | | 1 | 1 | 1 |00:00:00.01 | 1 | | | | | 6 | INDEX FULL SCAN (MIN/MAX)| EMP_DEPARTMENT_IX | 1 | 1 | 1 |00:00:00.01 | 1 | | | | ---------------------------------------------------------------------------------------------------------------------------------

!

! :/!$%B-?(/$!(>%!X7%$+!(>6&!A-+!4-J%&!&%'&%K!)/%&'U(!6(R!F$-?G%!A6GG!7&%!(>%!5$/5%$!.Wi=.Is!/5(646[-(6/'!/B!(>%!6')%@!B7GG!&?-'K!*7(!/'G+!6B!(>%!X7%$+!'%%)&!/'%!-;;$%;-(%!V-G7%!B/$!%6(>%$!(>%!46'!/$!4-@!-(!-!(64%L!WB!+/7!'%%)!*/(>K!(>%!/5%$-(6/'!$%V%$(&!(/!-!5/&&6*G+!?/&(G+!B7GG!(-*G%!&?-'L!<6V6)6';!(>%!X7%$+!6'(/!(A/!&%5-$-(%!X7%$6%&!-GG/A&!+/7!(/!;%(!(>%!$%&7G(!+/7!A-'(!A6(>!46'64-G!$%&/7$?%!7&%L!

XB9B7 FQ"/5N+&7]&CNN&.,&#.#+&

! :>6&!%@-45G%!&>/A&!-!&G6;>(G+!)6BB%$%'(!A-+!6'!A>6?>!(>%!N)6V6)%!-')!?/'X7%$Q!-'(65-((%$'!/??7$&L!W'!(>6&!?-&%K!-!&6';G%!X7%$+!6&!7&%)!(/!$%($6%V%!)-(-!*7(!-!5$%)6?-(%!6&!5$%&%'(!(>-(!5$/V6)%&!-'!%6(>%$=/$!&6(7-(6/'L!M>-(!W!4%-'!*+!(>-(!6&!(>-(!B/$!&/4%!B6G(%$!V-G7%&K!(>%!X7%$+!A6GG!'%%)!(/!$%(7$'!-GG!$/A&!-')!B/$!/(>%$!B6G(%$!V-G7%&K!(>%!X7%$+!A6GG!$%(7$'!-!G646(%)!&7*&%(L!!

:>%$%!6&!/'%!/(>%$!?/')6(6/'!(>-(!47&(!*%!4%(!B/$!(>6&!-'(65-((%$'!(/!*%!4-(?>%)L!:>%!B6G(%$!V-G7%!(>-(!6&!(>%!)%(%$46'%$!B/$!$%(7$'6';!-GG!$/A&!/$!5-$(6-G!$/A&!6&'U(!6'!(>%!(-*G%!B$/4!A>6?>!+/7!A-'(!(/!$%(7$'!(>%!$/A&L!:>6&!4-+!&/7')!-!*6(!?/'B7&6';!&/!G%(U&!G//J!-(!-'!%@-45G%L!

SELECT /* korig */ cd.cust_sys_id, cd.clup_sys_id FROM td_clup_detail_mv cd JOIN td_row_level_security_mv rl ON cd.clup_sys_id = rl.clup_sys_id JOIN td_individual_mv i ON rl.user_sys_id = i.indiv_sys_id WHERE doc_type_cd IN ('AGENCY', 'FNAS-LNR') AND agent_stat_cd = 'ACTIVE' AND i.user_login_id = 'Reporting'

!

I&!+/7!?-'!&%%K!(>%!/'G+!(-*G%!A%!A-'(!$/A&!B$/4!6&!:<jCPT#j<E:IWPj.gL!Y7(K!6'!/$)%$!(/!)%(%$46'%!A>6?>!$/A&!(/!$%(7$'K!(>%$%!6&!-!]/6'!(/!-!&%?7$6(+!(-*G%L!:>%!$/A&!6'!(>%!&%?7$6(+!(-*G%!(>-(!-$%!-V-6G-*G%!-$%!)%(%$46'%)!*+!(>%!B6G(%$!-;-6'&(!7&%$jG/;6'j6)L!C%$(-6'!7&%$&K!*-&%)!/'!(>%6$!-7(>/$6[-(6/'!G%V%GK!-$%!-*G%!(/!-??%&&!-GG!)-(-!A>6G%!/(>%$!7&%$&!A6GG!*%!$%&($6?(%)!(/!/'G+!-!&4-GG%$!&7*&%(!/B!)-(-L!:>%!(-*G%!?/'(-6'6';!(>6&!-7(>/$6[-(6/'!?/)%!6&!:<jWi<WgW<TIPj.gL!W'!(>%!X7%$+K!(>%!7&%$U&!G/;6'!6)!6&!7&%)!(/!$%($6%V%!(>%!$/A&!B$/4!(>%!&%?7$6(+!(-*G%L!Y7(K!>%$%U&!A>%$%!(>%!6&&7%!5/5&!75L!M>-(!6B!(>%!7&%$!6&!7'$%&($6?(%)!-')!?-'!&%%!-GG!(>%!)-(-R!W'!(>-(!?-&%K!+/7!-$%!$%-)6';!5/(%'(6-GG+!(>/7&-')&!/B!$/A&!6'!(>%!&%?7$6(+!(-*G%!(>-(!)/'U(!'%%)!(/!*%!$%-)L!IGG!+/7!$%-GG+!'%%)!(/!)/!6&!(/!$%-)!(>%!(-*G%!B$/4!A>6?>!+/7!'%%)!(/!$%($6%V%!$/A&L!

:>%!%@%?7(6/'!5G-'!&>/A&!+/7!(>%!A/$J!$%X76$%)!(/!$%-)!(>%!&%?7$6(+!(-*G%L!

SQL> @pln korig SQL_ID bjasak8wj3q9v, child number 0 ------------------------------------- Plan hash value: 1846763385 ------------------------------------------------------------------------------------------------------------------------------------

Page 24: Managing SQL Performance

! ! !

!

! ! ! ! ! ! ! ! ! ! !0\!

| Id | Operation | Name |Starts |A-Rows | A-Time |Buffers |OMem |1Mem |Used-Mem | ------------------------------------------------------------------------------------------------------------------------------------ |* 1 | HASH JOIN | | 1 | 20908 |00:00:23.46 | 55968 |4042K|1659K|6226K (0)| | 2 | MAT_VIEW ACCESS BY INDEX ROWID | TD_ROW_LEVEL_SECURITY_MV | 1 | 118K|00:00:22.45 | 53856 | | | | | 3 | NESTED LOOPS | | 1 | 118K|00:00:00.24 | 136 | | | | | 4 | MAT_VIEW ACCESS BY INDEX ROWID| TD_INDIVIDUAL_MV | 1 | 1 |00:00:00.01 | 3 | | | | |* 5 | INDEX RANGE SCAN | TD_INDIVIDUAL_N1 | 1 | 1 |00:00:00.01 | 2 | | | | |* 6 | INDEX RANGE SCAN | TD_ROW_LEVEL_SECURITY_MV_N1| 1 | 118K|00:00:00.17 | 133 | | | | |* 7 | VIEW | index$_join$_001 | 1 | 20908 |00:00:00.71 | 2112 | | | | |* 8 | HASH JOIN | | 1 | 20908 |00:00:00.68 | 2112 |1636K|1091K|2215K (0)| |* 9 | HASH JOIN | | 1 | 20908 |00:00:00.28 | 173 |2587K|1183K|3910K (0)| |* 10 | INDEX RANGE SCAN | TD_CLUP_DETAIL_MV_N3 | 1 | 47734 |00:00:00.01 | 61 | | | | | 11 | INLIST ITERATOR | | 1 | 88001 |00:00:00.09 | 112 | | | | |* 12 | INDEX RANGE SCAN | TD_CLUP_DETAIL_MV_N14 | 2 | 88001 |00:00:00.01 | 112 | | | | |* 13 | INDEX FAST FULL SCAN | TD_CLUP_DETAIL_MV_IDX1 | 1 | 88001 |00:00:00.18 | 1939 | | | | ------------------------------------------------------------------------------------------------------------------------------------ Predicate Information (identified by operation id): --------------------------------------------------- 1 - access("CD"."CLUP_SYS_ID"="RL"."CLUP_SYS_ID") 5 - access("I"."USER_LOGIN_ID"='Reporting') 6 - access("RL"."USER_SYS_ID"="I"."INDIV_SYS_ID") 7 - filter(("CD"."AGENT_STAT_CD"='ACTIVE' AND INTERNAL_FUNCTION("CD"."DOC_TYPE_CD"))) 8 - access(ROWID=ROWID) 9 - access(ROWID=ROWID) 10 - access("CD"."AGENT_STAT_CD"='ACTIVE') 12 - access(("CD"."DOC_TYPE_CD"='AGENCY' OR "CD"."DOC_TYPE_CD"='FNAS-LNR')) 13 - filter(("CD"."DOC_TYPE_CD"='AGENCY' OR "CD"."DOC_TYPE_CD"='FNAS-LNR'))

!

W'!(>6&!?-&%K!(>%!7&%$!A-&!7'$%&($6?(%)L!WB!(>%!7&%$!A-&!$%&($6?(%)K!(>%!645-?(!/B!>6((6';!(>%!&%?7$6(+!(-*G%!A/7G)'U(!*%!X76(%!&/!*-)K!*%?-7&%!/'G+!-!G646(%)!'74*%$!/B!$/A&!A6GG!'%%)!(/!*%!&%G%?(%)!B$/4!6(!-&!B/GG/A&H!

SQL> @pln korig2 SQL_ID 5zuyphy3v87rm, child number 0 ------------------------------------- Plan hash value: 1846763385 --------------------------------------------------------------------------------------------------------------------------- | Id | Operation | Name |Starts |A-Rows | A-Time |Buffers |OMem |1Mem | --------------------------------------------------------------------------------------------------------------------------- |* 1 | HASH JOIN | | 1 | 1199 |00:00:00.71 | 10908 |1245K|1245K| | 2 | MAT_VIEW ACCESS BY INDEX ROWID | TD_ROW_LEVEL_SECURITY_MV | 1 | 11507 |00:00:00.12 | 10106 | | | | 3 | NESTED LOOPS | | 1 | 11509 |00:00:00.02 | 18 | | | | 4 | MAT_VIEW ACCESS BY INDEX ROWID| TD_INDIVIDUAL_MV | 1 | 1 |00:00:00.01 | 3 | | | |* 5 | INDEX RANGE SCAN | TD_INDIVIDUAL_N1 | 1 | 1 |00:00:00.01 | 2 | | | |* 6 | INDEX RANGE SCAN | TD_ROW_LEVEL_SECURITY_MV_N1 | 1 | 11507 |00:00:00.02 | 15 | | | |* 7 | VIEW | index$_join$_001 | 1 | 20908 |00:00:00.53 | 802 | | | |* 8 | HASH JOIN | | 1 | 20908 |00:00:00.51 | 802 |1636K|1091K| |* 9 | HASH JOIN | | 1 | 20908 |00:00:00.28 | 173 |2587K|1183K| |* 10 | INDEX RANGE SCAN | TD_CLUP_DETAIL_MV_N3 | 1 | 47734 |00:00:00.01 | 61 | | | | 11 | INLIST ITERATOR | | 1 | 88001 |00:00:00.09 | 112 | | | |* 12 | INDEX RANGE SCAN | TD_CLUP_DETAIL_MV_N14 | 2 | 88001 |00:00:00.01 | 112 | | | |* 13 | INDEX FAST FULL SCAN | KM_TD_CLUP_DETAIL_MV_IDX1 | 1 | 88001 |00:00:00.09 | 629 | | | --------------------------------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - access("CD"."CLUP_SYS_ID"="RL"."CLUP_SYS_ID") 5 - access("I"."USER_LOGIN_ID"='[email protected]') 6 - access("RL"."USER_SYS_ID"="I"."INDIV_SYS_ID") 7 - filter(("CD"."AGENT_STAT_CD"='ACTIVE' AND INTERNAL_FUNCTION("CD"."DOC_TYPE_CD"))) 8 - access(ROWID=ROWID) 9 - access(ROWID=ROWID) 10 - access("CD"."AGENT_STAT_CD"='ACTIVE') 12 - access(("CD"."DOC_TYPE_CD"='AGENCY' OR "CD"."DOC_TYPE_CD"='FNAS-LNR')) 13 - filter(("CD"."DOC_TYPE_CD"='AGENCY' OR "CD"."DOC_TYPE_CD"='FNAS-LNR'))

!

Page 25: Managing SQL Performance

! ! ! ! !! !

! ! ! ! 0a!

:>%!$%B-?(/$%)!&/G7(6/'!A/7G)!'%%)!(/!5$/V6)%!-!A-+!(/!)%(%$46'%!(>%!-??%&&!G%V%G!-')!/'G+!%@%?7(%!(>%!]/6'!(/!(>%!&%?7$6(+!(-*G%!6B!(>%!7&%$U&!-7(>/$6[-(6/'!G%V%G!A-&!$%&($6?(%)!-')!*+5-&&!]/6'6';!(/!(>-(!(-*G%!6B!(>%!7&%$!A-&!7'$%&($6?(%)L!:>%!&/G7(6/'!6&H!

WITH /* krewrite */ access_lvl as ( SELECT row_access_auth_cd, indiv_sys_id FROM td_individual_mv WHERE user_login_id = 'Reporting' ) SELECT c.cust_sys_id, c.clup_sys_id FROM td_clup_detail_mv c, access_lvl a WHERE c.doc_type_cd IN ('AGENCY', 'FNAS-LNR') AND c.agent_stat_cd = 'ACTIVE' AND a.row_access_auth_cd = 'RESTRICTED' AND c.clup_sys_id in (select rl.clup_sys_id from td_row_level_security_mv rl where rl.user_sys_id = a.indiv_sys_id) UNION ALL SELECT c.cust_sys_id, c.clup_sys_id FROM td_clup_detail_mv c, access_lvl a WHERE c.doc_type_cd IN ('AGENCY', 'FNAS-LNR') AND c.agent_stat_cd = 'ACTIVE' AND a.row_access_auth_cd = 'UNRESTRICTED' SQL> @pln krewrite SQL_ID ghkvzdsb9d38w, child number 0 ------------------------------------- Plan hash value: 1290605034 ---------------------------------------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Starts |A-Rows | A-Time |Buffers |Reads | OMem | 1Mem | ---------------------------------------------------------------------------------------------------------------------------------------- | 1 | TEMP TABLE TRANSFORMATION | | 1 | 20908 |00:00:00.34 | 4763 | 1 | | | | 2 | LOAD AS SELECT | | 1 | 1 |00:00:00.05 | 7 | 0 | 265K| 265K| | 3 | MAT_VIEW ACCESS BY INDEX ROWID | TD_INDIVIDUAL_MV | 1 | 1 |00:00:00.01 | 3 | 0 | | | |* 4 | INDEX RANGE SCAN | TD_INDIVIDUAL_N1 | 1 | 1 |00:00:00.01 | 2 | 0 | | | | 5 | UNION-ALL | | 1 | 20908 |00:00:00.27 | 4753 | 1 | | | |* 6 | HASH JOIN | | 1 | 0 |00:00:00.01 | 6 | 1 | 733K| 733K| | 7 | NESTED LOOPS | | 1 | 0 |00:00:00.01 | 6 | 1 | | | |* 8 | VIEW | | 1 | 0 |00:00:00.01 | 6 | 1 | | | | 9 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6E18_BE82C8E3 | 1 | 1 |00:00:00.01 | 6 | 1 | | | | 10 | SORT UNIQUE | | 0 | 0 |00:00:00.01 | 0 | 0 |73728 |73728 | | 11 | MAT_VIEW ACCESS BY INDEX ROWID| TD_ROW_LEVEL_SECURITY_MV | 0 | 0 |00:00:00.01 | 0 | 0 | | | |* 12 | INDEX RANGE SCAN | TD_ROW_LEVEL_SECURITY_MV_N1 | 0 | 0 |00:00:00.01 | 0 | 0 | | | |* 13 | VIEW | index$_join$_002 | 0 | 0 |00:00:00.01 | 0 | 0 | | | |* 14 | HASH JOIN | | 0 | 0 |00:00:00.01 | 0 | 0 | 991K| 991K| |* 15 | HASH JOIN | | 0 | 0 |00:00:00.01 | 0 | 0 | 1088K| 1088K| |* 16 | INDEX RANGE SCAN | TD_CLUP_DETAIL_MV_N3 | 0 | 0 |00:00:00.01 | 0 | 0 | | | | 17 | INLIST ITERATOR | | 0 | 0 |00:00:00.01 | 0 | 0 | | | |* 18 | INDEX RANGE SCAN | TD_CLUP_DETAIL_MV_N14 | 0 | 0 |00:00:00.01 | 0 | 0 | | | |* 19 | INDEX FAST FULL SCAN | KM_TD_CLUP_DETAIL_MV_IDX1 | 0 | 0 |00:00:00.01 | 0 | 0 | | | | 20 | MERGE JOIN CARTESIAN | | 1 | 20908 |00:00:00.25 | 4747 | 0 | | | |* 21 | VIEW | | 1 | 1 |00:00:00.01 | 3 | 0 | | | | 22 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6E18_BE82C8E3 | 1 | 1 |00:00:00.01 | 3 | 0 | | | | 23 | BUFFER SORT | | 1 | 20908 |00:00:00.23 | 4744 | 0 | 1328K| 528K| |* 24 | MAT_VIEW ACCESS FULL | TD_CLUP_DETAIL_MV | 1 | 20908 |00:00:00.19 | 4744 | 0 | | | ---------------------------------------------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 4 - access("USER_LOGIN_ID"='Reporting') 6 - access("C"."CLUP_SYS_ID"="RL"."CLUP_SYS_ID") 8 - filter("A"."ROW_ACCESS_AUTH_CD"='RESTRICTED') 12 - access("RL"."USER_SYS_ID"="A"."INDIV_SYS_ID") 13 - filter(("C"."AGENT_STAT_CD"='ACTIVE' AND INTERNAL_FUNCTION("C"."DOC_TYPE_CD"))) 14 - access(ROWID=ROWID) 15 - access(ROWID=ROWID) 16 - access("C"."AGENT_STAT_CD"='ACTIVE') 18 - access(("C"."DOC_TYPE_CD"='AGENCY' OR "C"."DOC_TYPE_CD"='FNAS-LNR')) 19 - filter(("C"."DOC_TYPE_CD"='AGENCY' OR "C"."DOC_TYPE_CD"='FNAS-LNR')) 21 - filter("A"."ROW_ACCESS_AUTH_CD"='UNRESTRICTED') 24 - filter(("C"."AGENT_STAT_CD"='ACTIVE' AND INTERNAL_FUNCTION("C"."DOC_TYPE_CD")))

Page 26: Managing SQL Performance

! ! !

!

! ! ! ! ! ! ! ! ! ! !0D!

SQL> @pln krewrite3 SQL_ID cwuw5ccd5v0dz, child number 0 ------------------------------------- Plan hash value: 2804591148 ------------------------------------------------------------------------------------------------------------------------------------- | Id | Operation | Name |Starts |A-Rows | A-Time |Buffers |Reads |OMem |1Mem | ------------------------------------------------------------------------------------------------------------------------------------- | 1 | TEMP TABLE TRANSFORMATION | | 1 | 1199 |00:00:01.09 | 10925 | 15 | | | | 2 | LOAD AS SELECT | | 1 | 1 |00:00:00.01 | 7 | 1 | 265K| 265K| | 3 | MAT_VIEW ACCESS BY INDEX ROWID | TD_INDIVIDUAL_MV | 1 | 1 |00:00:00.01 | 3 | 1 | | | |* 4 | INDEX RANGE SCAN | TD_INDIVIDUAL_N1 | 1 | 1 |00:00:00.01 | 2 | 1 | | | | 5 | UNION-ALL | | 1 | 1199 |00:00:01.07 | 10915 | 14 | | | |* 6 | HASH JOIN | | 1 | 1199 |00:00:01.07 | 10912 | 14 |1238K|1100K| | 7 | NESTED LOOPS | | 1 | 11507 |00:00:00.46 | 10110 | 14 | | | |* 8 | VIEW | | 1 | 1 |00:00:00.01 | 6 | 1 | | | | 9 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6E23_BE82C8E3 | 1 | 1 |00:00:00.01 | 6 | 1 | | | | 10 | SORT UNIQUE | | 1 | 11507 |00:00:00.45 | 10104 | 13 | 690K| 433K| | 11 | MAT_VIEW ACCESS BY INDEX ROWID| TD_ROW_LEVEL_SECURITY_MV | 1 | 11507 |00:00:00.41 | 10104 | 13 | | | |* 12 | INDEX RANGE SCAN | TD_ROW_LEVEL_SECURITY_MV_N1 | 1 | 11507 |00:00:00.17 | 15 | 13 | | | |* 13 | VIEW | index$_join$_002 | 1 | 20908 |00:00:00.55 | 802 | 0 | | | |* 14 | HASH JOIN | | 1 | 20908 |00:00:00.53 | 802 | 0 |1895K|1011K| |* 15 | HASH JOIN | | 1 | 20908 |00:00:00.28 | 173 | 0 |2587K|1183K| |* 16 | INDEX RANGE SCAN | TD_CLUP_DETAIL_MV_N3 | 1 | 47734 |00:00:00.01 | 61 | 0 | | | | 17 | INLIST ITERATOR | | 1 | 88001 |00:00:00.09 | 112 | 0 | | | |* 18 | INDEX RANGE SCAN | TD_CLUP_DETAIL_MV_N14 | 2 | 88001 |00:00:00.01 | 112 | 0 | | | |* 19 | INDEX FAST FULL SCAN | KM_TD_CLUP_DETAIL_MV_IDX1 | 1 | 88001 |00:00:00.09 | 629 | 0 | | | | 20 | MERGE JOIN CARTESIAN | | 1 | 0 |00:00:00.01 | 3 | 0 | | | |* 21 | VIEW | | 1 | 0 |00:00:00.01 | 3 | 0 | | | | 22 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6E23_BE82C8E3 | 1 | 1 |00:00:00.01 | 3 | 0 | | | | 23 | BUFFER SORT | | 0 | 0 |00:00:00.01 | 0 | 0 |1249K| 518K| |* 24 | MAT_VIEW ACCESS FULL | TD_CLUP_DETAIL_MV | 0 | 0 |00:00:00.01 | 0 | 0 | | | ------------------------------------------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 4 - access("USER_LOGIN_ID"='[email protected]') 6 - access("C"."CLUP_SYS_ID"="RL"."CLUP_SYS_ID") 8 - filter("A"."ROW_ACCESS_AUTH_CD"='RESTRICTED') 12 - access("RL"."USER_SYS_ID"="A"."INDIV_SYS_ID") 13 - filter(("C"."AGENT_STAT_CD"='ACTIVE' AND INTERNAL_FUNCTION("C"."DOC_TYPE_CD"))) 14 - access(ROWID=ROWID) 15 - access(ROWID=ROWID) 16 - access("C"."AGENT_STAT_CD"='ACTIVE') 18 - access(("C"."DOC_TYPE_CD"='AGENCY' OR "C"."DOC_TYPE_CD"='FNAS-LNR')) 19 - filter(("C"."DOC_TYPE_CD"='AGENCY' OR "C"."DOC_TYPE_CD"='FNAS-LNR')) 21 - filter("A"."ROW_ACCESS_AUTH_CD"='UNRESTRICTED') 24 - filter(("C"."AGENT_STAT_CD"='ACTIVE' AND INTERNAL_FUNCTION("C"."DOC_TYPE_CD")))

!

:>%!$%&7G(!/B!(>%!$%B-?(/$%)!&/G7(6/'!6&!(>-(!+/7!;%(!-!&6';G%!5G-'!(>-(!A/$J&!A%GG!B/$!*/(>!(+5%&!/B!7&%$&L!:>%!/$6;6'-G!3OP!A/7G)!5$/V6)%!B-&(%$!$%&5/'&%!(64%&!B/$!7&%$&!A6(>!$%&($6?(%)!-??%&&!*7(!A/7G)!*%!&G/A!6'!?/45-$6&/'!B/$!7&%$&!A6(>!7'$%&($6?(%)!-??%&&L!!

,%%5!6'!46')!(>-(!(>%!N)6V6)%!-')!?/'X7%$Q!-'(65-((%$'!?-'!?/4%!6'!4-'+!BG-V/$&L!:>%!J%+!6&!(/!)%(%$46'%!6B!(>%!$%X76$%)!A/$J!?-'!*%!&5G6(!75!6'!&7?>!-!A-+!-&!(/!-GG/A!?/45%(6';!'%%)&!(/!*%!4%(L!:>%!(A/!%@-45G%&!A%UV%!$%V6%A%)!-$%!]7&(!(>%!(65!/B!(>%!6?%*%$;L!Z/7UGG!G6J%G+!B6')!4/$%!A-+&!(>6&!-'(65-((%$'!A6GG!4-'6B%&(!6'!+/7$!3OP!6B!+/7!)6;!)%%5%$!A6(>!-!N)6V6)%!-')!?/'X7%$Q!%+%L!

XB7 :H+&E"2N%#$&E6#&

:>%!Nd-(G6';!;7'Q!-'(65-((%$'!6&!%V6)%'?%)!*+!$%5%-(%)!-??%&&!(/!/*]%?(&!(>-(!&>/7G)!/'G+!'%%)!(/!*%!-??%&&%)!/'?%L!P6J%!-'!-?(7-G!d-(G6';!;7'K!(>6&!-'(65-((%$'!?-'!J6GG!5%$B/$4-'?%!A6(>!$%5%-(%)K!$-56)^B6$%!/*]%?(!-??%&&%&L!:>%$%!-$%!4-'+!BG-V/$&!/B!(>6&!-'(65-((%$'K!*7(!W!&%%!6(!4/&(!B$%X7%'(G+!$%5%-(%)!7&%!/B!&7*X7%$6%&!(>-(!?/457(%!-;;$%;-(%!V-G7%&L!

Page 27: Managing SQL Performance

! ! ! ! !! !

! ! ! ! 0c!

XB7B9 FQ"/5N+]&>+5+"2%#$&"$$,+$"2+3&

.-'+!-55G6?-(6/'&!'%%)!(/!B6')!$/A&!/B!)-(-!(>-(!4-(?>!-!?/')6(6/'!*-&%)!/'!-;;$%;-(6';!)-(-!6'!(>%!(-*G%!6'!/$)%$!(/!;%(!-!V-G7%!(/!?/45-$%!(/!%-?>!$/A!(/!)%(%$46'%!6B!(>%!$/A!&>/7G)!*%!6'?G7)%)!6'!(>%!$%&7G(!&%(L!:>%!%@-45G%!A%U$%!;/6';!(/!$%V6%A!6&!(+56?-GG+!&%%'!A>%'!+/7$!X7%$+!'%%)&!(/!B6')!$/A&!(>-(!4-(?>!-'!N-&!/BQ!)-(%L!

W'!(>6&!?-&%K!(>%!%'V6$/'4%'(!A-&!-!46@!/B!FP:#!-')!/'G6'%!$%5/$(6';!X7%$6%&L!.-'+!/B!(>%!$%5/$(6';!X7%$6%&!A/7G)!(-J%!>/7$&!(/!?/45G%(%!-')!(>6&!-)V%$&%G+!-BB%?(%)!FP:#!$%&5/'&%!(64%&L!:>%!B/GG/A6';!$%5/$(6';!X7%$+!(//J!/V%$!\!>/7$&!(/!$%(7$'!]7&(!/V%$!"eK888!$/A&L!M/Af!!

SELECT ATTRITION_FCTR, ATTRITION_FACTOR_CLNDR_YR, ATTRITION_FACTOR_CLNDR_MO, PGRD_PROJECT_CODE FROM ODS_PGRD_PROJECT_ATRTN_FACTORS A WHERE SIMULATION_CLNDR_YR || SIMULATION_CLNDR_MO = (SELECT MAX(B.SIMULATION_CLNDR_YR || B.SIMULATION_CLNDR_MO) FROM ODS_PGRD_PROJECT_ATRTN_FACTORS B WHERE A.PGRD_PROJECT_CODE = B.PGRD_PROJECT_CODE)

:>%!$%&5/'&%!(64%!5$/B6G%!-')!%@%?7(6/'!5G-'!&>/A%)!-GG!(>%!(64%!&5%'(!6'!B%(?>6';!$/A&L!

-- Profile (R = 4 hours 14 minutes 4.236 seconds) DB Call Duration CPU Other %R Rows LIO PIO ------------- ---------- ---------- ------- ----- ------ ---------- ---------- FETCH 15,238.249 15,297.950 -59.701 100% 18,480 52,756,900 38,490,001 Between Calls 5.943 0.000 5.943 0% 0 0 0 PARSE 0.032 0.030 0.002 0% 0 0 0 EXEC 0.012 0.000 0.012 0% 0 3 1 ------------- ---------- ---------- ------- ----- ------ ---------- ---------- Total 15,244.236 15,297.980 -53.744 100% 18,480 52,756,903 38,490,002 -- Execution Plan ID Row Source Operation Duration %R Rows LIO PIO -- ----------------------------------- ---------- ------ ---------- ---------- ---------- 1 FILTER -123.167 -0.8% 18,480 86,529 0 2 TABLE ACCESS FULL OBJ#(32390) 1.526 0.0% 271,830 3,053 7,588 3 SORT AGGREGATE 0.761 0.0% 28,843 0 0 4 SORT AGGREGATE 96.790 0.6% 28,843 0 0 5 TABLE ACCESS FULL OBJ#(32390) 15,093.126 100.2% 14,816,070 52,667,318 38,482,413 -- ----------------------------------- ---------- ------ ---------- ---------- ---------- 15,069.036 100.0% 52,756,900 38,490,001

! :>%!5G-'!&>/A&!(A/!B7GG!(-*G%!&?-'!/5%$-(6/'&!/'!(>%!F<3j#dS<j#SFbEC:jI:S:ijkIC:FS3!(-*G%L!WB!+/7!G//J!-(!(>%!X7%$+K!+/7UGG!'/(6?%!(>-(!(>%!5$%)6?-(%!?/'(-6'&!-!&7*^X7%$+!(/!?>%?J!B/$!(>%!4-@!)-(%!B/$!(>%!5$/]%?(!*-&%)!/'!(>%!5$/]%?(!?/)%!B$/4!(>%!?7$$%'(!5$/]%?(L!:>%$%!-$%!(A/!5$/*G%4&L!!

k6$&(K!(>%!(-*G%!>-&!0c"Ke18!$/A&K!&/!B/$!%-?>!$/AK!(>%!&7*^X7%$+!4-+!'%%)!(/!%@%?7(%L!i/(6?%!(>-(!W!&-+!N4-+!'%%)!(/!%@%?7(%Q!>%$%L!:>-(U&!*%?-7&%!WU)!&7&5%?(!(>%!X7%$+!A6GG!*%'%B6(!B$/4!&/4%!&7*^X7%$+!?-?>6';!-')!'/(!$%-GG+!>-V%!(/!%@%?7(%!(>%!X7%$+!/V%$!-')!/V%$!B/$!$%5%-(%)!V-G7%&L!Y7(K!%-?>!(64%!6(!)/%&!%@%?7(%K!6(!A6GG!$%X76$%!-!B7GG!(-*G%!&?-'L!W(!&%%4&!(>-(!6B!-'!6')%@!/'!#dS<j#SFbEC:jCF<E!_-')!3W.TPI:WFijCPi<SjZS!-')!3W.TPI:WFijCPi<Sj.F`!A-&!-V-6G-*G%K!(>%!X7%$+!A/7G)!*%'%B6(!*+!-(!G%-&(!*%6';!-*G%!(/!7&%!(>%!6')%@!(/!;%(!(>%!4-@!V-G7%L!:>%!$%-&/'!6(!A-&'U(!(>%$%!A-&!(>-(!(>%+!>-)!-!$7G%!(>-(!(>%+!/'G+!?$%-(%!&6';G%!?/G74'!6')%@%&L!W!*%G6%V%!(>6&!A-&!-!>/G)/V%$!B$/4!A>%'!(>%!)-(-*-&%!A-&!/'G+!7&%)!B/$!FP:#!-')!-!B%A!/(>%$!N;//)Q!$%-&/'&K!*7(!(>6&!$7G%!A-&!5$/>6*6(6';!(>%4!B$/4!/5(646[6';!'/(!/'G+!(>6&!3OPK!*7(!G/(&!4/$%!-&!A%GGL!

:>%!&%?/')!5$/*G%4!6&!)7%!(/!(>%!&-4%!&7*^X7%$+L!:>%!47G(65G%!%@%?7(6/'&!/B!(>%!3EPEC:!.Is!X7%$+!4%-'(!47G(65G%!V6&6(&!(/!(>%!&-4%!(-*G%!)$6V6';!(>%!X7%$+L!EV%'!6B!(>%!6')%@!>-)!*%%'!6'!5G-?%K!(>%!$%5%-(%)!

Page 28: Managing SQL Performance

! ! !

!

! ! ! ! ! ! ! ! ! ! !0e!

V6&6(&!(/!(>%!&-4%!/*]%?(!4%-'&!(>-(!(>%!&-4%!*G/?J&!A6GG!'%%)!(/!*%!($-V%$&%)!/V%$!-')!/V%$L!M>+!'/(!($-V%$&%!(>%!*G/?J&!]7&(!/'?%R!

:>6&!A-&!-!?>-';%!A%!?/7G)!645G%4%'(f!W'!/$)%$!(/!?>-';%!(>%!X7%$+!(/!4-J%!-!&6';G%!5-&&!/V%$!(>%!(-*G%!A>6G%!-GG/A6';!(>%!4-@!?/45-$6&/'!(/!*%!>-')G%)K!A%!&645G+!$%A$/(%!(>%!X7%$+!(/!7&%!-'!-'-G+(6?!.Is!-&!B/GG/A&H!

SELECT a.ATTRITION_FCTR, a.ATTRITION_FACTOR_CLNDR_YR, a.ATTRITION_FACTOR_CLNDR_MO, a.PGRD_PROJECT_CODE FROM ( SELECT MAX(simulation_clndr_yr || simulation_clndr_mo) over (partition by pgrd_project_code) as the_max,

ATTRITION_FCTR, ATTRITION_FACTOR_CLNDR_YR, ATTRITION_FACTOR_CLNDR_MO, PGRD_PROJECT_CODE, SIMULATION_CLNDR_YR || SIMULATION_CLNDR_MO as the_yrmo FROM ODS_PGRD_PROJECT_ATRTN_FACTORS ) a WHERE a.the_yrmo = a.the_max ;

! Y+!$%B-?(/$6';!(>%!3OP!(/!4-J%!-!&6';G%!5-&&!/V%$!(>%!(-*G%!-')!?/457(%!(>%!.Is!V-G7%!-&!-!&%5-$-(%!?/G74'!7&6';!(>%!-'-G+(6?K!A%!A%$%!-*G%!(/!-V/6)!$%5%-(%)!-??%&&%&!(/!(>%!(-*G%!-')!$%)7?%!(>%!A/$J!$%X76$%)!(/!]7&(!-!B$-?(6/'!/B!(>%!/$6;6'-GL!

-- Profile (R = 16.989 seconds) DB Call Duration CPU Other %R Rows LIO PIO ------------- -------- ------- ------- ----- ------ ----- ----- FETCH 11.662 11.110 0.552 68.6% 18,480 1,828 4,680 Between Calls 5.030 0.000 5.030 29.6% 0 0 0 EXEC 0.272 0.030 0.242 1.6% 0 3 1 PARSE 0.026 0.030 -0.004 0.2% 0 6 0 ------------- -------- ------- ------- ----- ------ ----- ----- Total 16.989 11.170 5.819 100% 18,480 1,837 4,681 -- Execution Plan ID Row Source Operation Duration %R Rows LIO PIO/R PIO/W -- ----------------------------------- ---------- ------ ------- ----- ----- ----- 1 VIEW 1.742 15.2% 18,480 0 0 0 2 WINDOW SORT 8.127 70.9% 271,830 7 2,861 1,340 3 TABLE ACCESS FULL OBJ#(32390) 1.598 13.9% 271,830 1,826 1,819 0 -- ----------------------------------- ---------- ------ ------- ----- ----- ----- 11.467 100.0% 1,833 4,680 1,340

! k$/4!-G4/&(!\!>/7$&!"a!46'7(%&!)/A'!(/!]7&(!7')%$!"c!&%?/')&L!IA%&/4%f!I')K!(>%+!A%$%!%V%'!)/7*G+!>-55+!(>-(!(>6&!?/7G)!*%!)/'%!A6(>/7(!>-V6';!(/!?$%-(%!-'+!6')%@%&L!!

XBM >+_%#I+#2%#$&2H+&KH++N&

:>6&!-'(65-((%$'!6&!6'!7&%!A>%'!-!3OP!&(-(%4%'(!>-&!*%%'!A$6((%'!(/!5$/V6)%!&/4%!B7'?(6/'-G6(+!(>-(!6&!-V-6G-*G%!6'!-!*76G(^6'!B7'?(6/'L!WU4!'/(!&7$%!A>-(!6(!6&!-*/7(!)-(%!4-(>!(>-(!&%%4&!(/!*%!(>%!5$64-$+!?7G5$6(!B/$!(>6&K!*7(!W!&%%4!(/!?/4%!-?$/&&!-!G/(!/B!G%';(>+!?/)%!(>-(!?/7G)!*%!$%5G-?%)!*+!-!&>/$(!-')!&645G%!*76G(^6'!B%-(7$%L!!

XBMB9 FQ"/5N+]&A6,,+#2&W,^*,%.,&W,&`",%"#0+&>+5.,2&

W!A-&!>-')%)!-!B-6$G+!G%';(>+!-')!?/45G%@!G//J6';!3OP!&(-(%4%'(!(>-(!(+56?-GG+!%@%?7(%)!6'!(>%!a^"8!46'7(%!$-';%L!:>%!/'G6'%!$%5/$(6';!(//G!>-)!-!a!46'7(%!(64%/7(!G646(!&%(!&7?>!(>-(!-'+!$%5/$(!(>-(!(//J!G/';%$!(>-'!a!46'7(%&!(/!?/45G%(%!A/7G)!*%!?-'?%GG%)!-')!(>%!7&%$!A/7G)!*%!$%X76$%)!(/!&?>%)7G%!(>%!]/*!(/!*%!

Page 29: Managing SQL Performance

! ! ! ! !! !

! ! ! ! 0m!

?/45G%(%)!6'!/BB^5%-J!>/7$&L!!:>%!$%5/$(!A-&!A>-(!&%%4%)!(/!*%!-!B-6$G+!&645G%!G6&(6';!(>-(!&>/A%)!-!?/45-$6&/'!/B!(>%!?7$$%'(!+%-$^(/^)-(%!$%V%'7%!(/!(>%!5$6/$!+%-$U&!$%V%'7%!B/$!(>%!&-4%!(64%!-G/';!A6(>!(>%!?/457(%)!Z:<!-')!X7-$(%$G+!V-$6-'?%&L!:>%!X7%$+!A-&!%@%?7(%)!V6-!C$+&(-G!S%5/$(&!-')!(>%!/7(57(!B/$4-((%)!*%B/$%!)6&5G-+L!I!(+56?-G!$%&7G(!&%(!B$/4!(>6&!X7%$+!46;>(!G//J!G6J%!(>6&H!

ROLLUP_CD CUST_LOC_NM LOC_CITY_NM LOC_REP_NM PRINCIPAL_NM CURR_YR_YTD PRIOR_YR_YTD YTD_VARIANCE QVARIANCE ---------- --------------- ------------------ -------------- ------------------- ----------- ------------ ------------ ---------- CLTIC John Q Public Jacksonville Jay Peterman S. Jacob O'Banyon 12356.24 7105.15 5251.09 -3736.5 CTIC John Q Public Jacksonville Jay Peterman S. Jacob O'Banyon 93109.05 84158.73 8950.32 -4687.17 FNTIC John Q Public Jacksonville Jay Peterman S. Jacob O'Banyon 88757.46 182125.08 -93367.62 -1168.12 CTIC John Q Public Ponte Vedra Beach Winfield Dunn S. Jacob O'Banyon 5963.18 2667.05 3296.13 -2897.94 FNTIC John Q Public Ponte Vedra Beach Winfield Dunn S. Jacob O'Banyon 0 0 0 0 CLTIC John Q Public Saint Augustine Jay Peterman S. Jacob O'Banyon 0 1522.5 -1522.5 0 CTIC John Q Public Saint Augustine Jay Peterman S. Jacob O'Banyon 18278.32 25274.69 -6996.37 -2534.61 FNTIC John Q Public Saint Augustine Jay Peterman S. Jacob O'Banyon 3907.13 1770.58 2136.55 0

:>%!/7(57(!G//J&!&645G%!%'/7;>K!&/!W!A/')%$%)!A>+!6(!A/7G)!(-J%!&/!G/';!(/!?/45G%(%L!F'%!G//J!-(!(>%!3OP!-')!W!>-)!4+!-'&A%$H!

WITH /* k2orig */ curr_period AS (SELECT t3.time_wh_sys_id, t3.acct_period_dt, t3.acct_month_year ,t3.acct_year ,CASE WHEN t3.acct_month IN ('1', '2', '3') THEN '1' WHEN t3.acct_month IN ('4', '5', '6') THEN '2' WHEN t3.acct_month IN ('7', '8', '9') THEN '3' WHEN t3.acct_month IN ('10', '11', '12') THEN '4' END curr_qtr FROM tw_dim_time t3 , (SELECT MAX (time_wh_sys_id) time_wh_sys_id FROM tw_dim_time WHERE acct_month_year <= '200912' AND is_acct_end_of_month = '1' AND actual_date < SYSDATE) t WHERE t3.time_wh_sys_id = t.time_wh_sys_id) ,curr_q_periods AS (SELECT CASE curr_period.curr_qtr WHEN '4' THEN (SELECT t.acct_month_year FROM tw_dim_time t WHERE t.acct_month IN ('10') AND t.acct_year = curr_period.acct_year AND t.is_acct_end_of_month = '1') WHEN '1' THEN (SELECT t.acct_month_year FROM tw_dim_time t WHERE t.acct_month IN ('1') AND t.acct_year = curr_period.acct_year AND t.is_acct_end_of_month = '1') WHEN '2' THEN (SELECT t.acct_month_year FROM tw_dim_time t WHERE t.acct_month IN ('4') AND t.acct_year = curr_period.acct_year AND t.is_acct_end_of_month = '1') WHEN '3' THEN (SELECT t.acct_month_year FROM tw_dim_time t WHERE t.acct_month IN ('7') AND t.acct_year = curr_period.acct_year AND t.is_acct_end_of_month = '1') END begin_curr_qtr ,CASE curr_period.curr_qtr WHEN '4' THEN (SELECT t.acct_month_year FROM tw_dim_time t WHERE t.acct_month IN ('12') AND t.acct_year = curr_period.acct_year AND t.is_acct_end_of_month = '1')

Page 30: Managing SQL Performance

! ! !

!

! ! ! ! ! ! ! ! ! ! !18!

WHEN '1' THEN (SELECT t.acct_month_year FROM tw_dim_time t WHERE t.acct_month IN ('3') AND t.acct_year = curr_period.acct_year AND t.is_acct_end_of_month = '1') WHEN '2' THEN (SELECT t.acct_month_year FROM tw_dim_time t WHERE t.acct_month IN ('6') AND t.acct_year = curr_period.acct_year AND t.is_acct_end_of_month = '1') WHEN '3' THEN (SELECT t.acct_month_year FROM tw_dim_time t WHERE t.acct_month IN ('9') AND t.acct_year = curr_period.acct_year AND t.is_acct_end_of_month = '1') END end_curr_qtr FROM curr_period) ,prior_period AS (SELECT t2.time_wh_sys_id prior_yr_time_sys_id ,t2.acct_month_year prior_yr_period FROM tw_dim_time t2, curr_period WHERE t2.acct_period_dt = ADD_MONTHS (curr_period.acct_period_dt, -12) AND is_acct_end_of_month = '1') ,prior_qtr AS (SELECT DECODE (curr_period.curr_qtr ,'1', '4' ,'2', '1' ,'3', '2' ,'4', '3' ) prior_qtr ,DECODE (curr_period.curr_qtr ,'1', curr_period.acct_year - 1 ,curr_period.acct_year ) prior_qtr_year FROM curr_period) ,prior_q_periods AS (SELECT CASE pq.prior_qtr WHEN '4' THEN (SELECT t.acct_month_year FROM tw_dim_time t WHERE t.acct_month IN ('10') AND t.acct_year = pq.prior_qtr_year AND t.is_acct_end_of_month = '1') WHEN '1' THEN (SELECT t.acct_month_year FROM tw_dim_time t WHERE t.acct_month IN ('1') AND t.acct_year = pq.prior_qtr_year AND t.is_acct_end_of_month = '1') WHEN '2' THEN (SELECT t.acct_month_year FROM tw_dim_time t WHERE t.acct_month IN ('4') AND t.acct_year = pq.prior_qtr_year AND t.is_acct_end_of_month = '1') WHEN '3' THEN (SELECT t.acct_month_year FROM tw_dim_time t WHERE t.acct_month IN ('7') AND t.acct_year = pq.prior_qtr_year AND t.is_acct_end_of_month = '1') END begin_prior_qtr ,CASE pq.prior_qtr WHEN '4' THEN (SELECT t.acct_month_year FROM tw_dim_time t WHERE t.acct_month IN ('12') AND t.acct_year = pq.prior_qtr_year AND t.is_acct_end_of_month = '1') WHEN '1' THEN (SELECT t.acct_month_year FROM tw_dim_time t WHERE t.acct_month IN ('3')

Page 31: Managing SQL Performance

! ! ! ! !! !

! ! ! ! 1"!

AND t.acct_year = pq.prior_qtr_year AND t.is_acct_end_of_month = '1') WHEN '2' THEN (SELECT t.acct_month_year FROM tw_dim_time t WHERE t.acct_month IN ('6') AND t.acct_year = pq.prior_qtr_year AND t.is_acct_end_of_month = '1') WHEN '3' THEN (SELECT t.acct_month_year FROM tw_dim_time t WHERE t.acct_month IN ('9') AND t.acct_year = pq.prior_qtr_year AND t.is_acct_end_of_month = '1') END end_prior_qtr FROM prior_qtr pq) ,time_slices AS (SELECT curr_period.time_wh_sys_id, curr_period.acct_month_year ,curr_period.curr_qtr, prior_period.prior_yr_time_sys_id ,prior_period.prior_yr_period, prior_q_periods.begin_prior_qtr ,prior_q_periods.end_prior_qtr, prior_qtr.prior_qtr ,prior_qtr.prior_qtr_year, curr_q_periods.begin_curr_qtr ,curr_q_periods.end_curr_qtr FROM curr_period ,prior_period ,prior_qtr ,prior_q_periods ,curr_q_periods) ,loc_details AS (SELECT cd.rollup_uw_abbv_cd ,CASE WHEN cd.cust_loc_nm IS NOT NULL AND cd.dba_nm_from_loc_nm_ind = 1 THEN cd.cust_loc_nm WHEN cd.cust_doing_bus_as_nm IS NOT NULL THEN cd.cust_doing_bus_as_nm ELSE cd.cust_bus_nm END cust_loc_nm ,cd.loc_city_nm , cd.loc_rep_f_nm || ' ' || cd.loc_rep_mid_int_nm || ' ' || cd.loc_rep_l_nm AS loc_rep_nm , (SELECT NVL (con.first_nm, '') || ' ' || NVL (con.mid_int_nm, '') || ' ' || NVL (con.last_nm, '') || ' ' || NVL (con.sufx_nm, '') FROM td_contact_mv con WHERE con.loc_sys_id = cd.loc_sys_id AND UPPER (con.position_type_cd) = 'PRINCIPAL' AND ROWNUM = 1) principal_nm ,cd.clup_sys_id FROM td_clup_detail_mv cd JOIN td_row_level_security_mv rl ON cd.clup_sys_id = rl.clup_sys_id JOIN td_individual_mv i ON rl.user_sys_id = i.indiv_sys_id WHERE cd.doc_type_cd IN ('AGENCY', 'FNAS-LNR') AND cd.cust_sys_id = 5922 AND i.user_login_id = 'TraxReporting') SELECT DISTINCT rollup_uw_abbv_cd, cust_loc_nm, loc_city_nm, loc_rep_nm ,principal_nm, NVL (SUM (curr_ytd_net), 0) curr_ytd_net ,acct_month_year AS curr_yr_period, curr_qtr ,NVL (SUM (prior_ytd_net), 0) prior_ytd_net, prior_yr_period ,prior_qtr, prior_qtr_year, begin_prior_qtr, end_prior_qtr ,NVL (SUM (prior_q_net_prm.prior_q_net), 0) prior_q_net ,begin_curr_qtr, end_curr_qtr ,NVL (SUM (curr_q_net_prm.curr_q_net), 0) curr_q_net FROM time_slices ,loc_details ,tw_dim_clup clup , (SELECT NVL(SUM (fcm.net_prm_tot_ytd_eom_amt),0) curr_ytd_net

Page 32: Managing SQL Performance

! ! !

!

! ! ! ! ! ! ! ! ! ! !10!

,fcm.time_wh_sys_id, fcm.clup_wh_sys_id FROM tw_fact_clup_monthly fcm JOIN time_slices ts ON fcm.time_wh_sys_id = ts.time_wh_sys_id GROUP BY fcm.time_wh_sys_id, fcm.clup_wh_sys_id) curr_ytd_net_prm , (SELECT NVL(SUM (fcm.net_prm_tot_ytd_eom_amt),0) prior_ytd_net ,fcm.time_wh_sys_id, fcm.clup_wh_sys_id FROM tw_fact_clup_monthly fcm JOIN time_slices ts ON fcm.time_wh_sys_id = ts.prior_yr_time_sys_id GROUP BY fcm.time_wh_sys_id, fcm.clup_wh_sys_id) prior_ytd_net_prm , (SELECT NVL (SUM (fcm.net_prm_tot_eom_amt), 0) prior_q_net ,fcm.clup_wh_sys_id FROM tw_fact_clup_monthly fcm JOIN tw_dim_time t5 ON fcm.time_wh_sys_id = t5.time_wh_sys_id JOIN prior_q_periods pq ON t5.acct_month_year BETWEEN pq.begin_prior_qtr AND pq.end_prior_qtr GROUP BY fcm.clup_wh_sys_id) prior_q_net_prm , (SELECT NVL (SUM (fcm.net_prm_tot_eom_amt), 0) curr_q_net ,fcm.clup_wh_sys_id FROM tw_fact_clup_monthly fcm JOIN tw_dim_time t6 ON fcm.time_wh_sys_id = t6.time_wh_sys_id JOIN curr_q_periods cq ON t6.acct_month_year BETWEEN cq.begin_curr_qtr AND cq.end_curr_qtr GROUP BY fcm.clup_wh_sys_id) curr_q_net_prm WHERE loc_details.clup_sys_id = clup.clup_sys_id AND clup.clup_wh_sys_id = curr_ytd_net_prm.clup_wh_sys_id(+) AND clup.clup_wh_sys_id = prior_ytd_net_prm.clup_wh_sys_id(+) AND clup.clup_wh_sys_id = prior_q_net_prm.clup_wh_sys_id(+) AND clup.clup_wh_sys_id = curr_q_net_prm.clup_wh_sys_id(+) GROUP BY rollup_uw_abbv_cd,cust_loc_nm,loc_city_nm ,loc_rep_nm,principal_nm,acct_month_year,curr_qtr,prior_yr_period,

prior_qtr,prior_qtr_year,begin_prior_qtr,end_prior_qtr,begin_curr_qtr,end_curr_qtr;

W!>-(%!(/!7&%!(>-(!47?>!$%-G!%&(-(%!(/!&>/A!+/7!-GG!(>-(K!*7(!W!A-'(%)!+/7!(/!;%(!(>%!B7GG!%BB%?(L!WB!+/7!(-J%!(>%!(64%!(/!A-GJ!(>$/7;>!A>-(!(>-(!3OP!6&!($+6';!(/!)/K!+/7UGG!B6')!(>-(!(>%!4-]/$6(+!/B!(>%!?/)%!A-&!5$%&%'(!/'G+!(/!>-')G%!)%(%$46'6';!(>%!?7$$%'(!-')!5$6/$!+%-$^(/^)-(%!(64%!&G6?%&!-')!(>%'!?/457(%!(>%!(/(-G&!'%%)%)!A6(>6'!%-?>!(64%!&G6?%L!:>%!%@%?7(6/'!5G-'!&>/A%)!A>%$%!(>%!(64%!A-&!&5%'(H!

SQL_ID ayp95f5dz662x, child number 0 ------------------------------------- Plan hash value: 1406955805 ---------------------------------------------------------------------------------------------------------------------------------- | Id | Operation | Name |Starts |A-Rows | A-Time |Buffers |Reads | ---------------------------------------------------------------------------------------------------------------------------------- |* 1 | COUNT STOPKEY | | 3 | 3 |00:00:00.03| 10 | 3 | |* 2 | MAT_VIEW ACCESS BY INDEX ROWID | TD_CONTACT_MV | 3 | 3 |00:00:00.03| 10 | 3 | |* 3 | INDEX RANGE SCAN | TD_CONTACT_MV_FK3 | 3 | 3 |00:00:00.02| 6 | 2 | | 4 | TEMP TABLE TRANSFORMATION | | 1 | 8 |00:04:19.91| 293K| 291K| | 5 | LOAD AS SELECT | | 1 | 1 |00:00:00.01| 27 | 0 | | 6 | NESTED LOOPS | | 1 | 1 |00:00:00.01| 23 | 0 | | 7 | VIEW | | 1 | 1 |00:00:00.01| 20 | 0 | | 8 | SORT AGGREGATE | | 1 | 1 |00:00:00.01| 20 | 0 | |* 9 | TABLE ACCESS BY INDEX ROWID | TW_DIM_TIME | 1 | 216 |00:00:00.01| 20 | 0 | |* 10 | INDEX RANGE SCAN | TW_DIM_TIME_N2 | 1 | 240 |00:00:00.01| 3 | 0 | | 11 | TABLE ACCESS BY INDEX ROWID | TW_DIM_TIME | 1 | 1 |00:00:00.01| 3 | 0 | |* 12 | INDEX UNIQUE SCAN | TW_DIM_TIME_EOM_PK | 1 | 1 |00:00:00.01| 2 | 0 | | 13 | LOAD AS SELECT | | 1 | 1 |00:00:00.01| 50 | 1 | | 14 | VIEW | | 1 | 1 |00:00:00.01| 6 | 1 | | 15 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6D31_64822175 | 1 | 1 |00:00:00.01| 6 | 1 | | 16 | LOAD AS SELECT | | 1 | 1 |00:00:00.01| 7 | 0 | | 17 | VIEW | | 1 | 1 |00:00:00.01| 3 | 0 | | 18 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6D31_64822175 | 1 | 1 |00:00:00.01| 3 | 0 | | 19 | LOAD AS SELECT | | 1 | 1 |00:00:00.01| 50 | 1 | | 20 | VIEW | | 1 | 1 |00:00:00.01| 6 | 1 | | 21 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6D33_64822175 | 1 | 1 |00:00:00.01| 6 | 1 | | 22 | LOAD AS SELECT | | 1 | 1 |00:00:00.01| 45 | 2 |

Page 33: Managing SQL Performance

! ! ! ! !! !

! ! ! ! 11!

|* 23 | TABLE ACCESS BY INDEX ROWID | TW_DIM_TIME | 1 | 1 |00:00:00.01| 41 | 2 | | 24 | NESTED LOOPS | | 1 | 242 |00:00:00.01| 24 | 2 | | 25 | MERGE JOIN CARTESIAN | | 1 | 1 |00:00:00.01| 21 | 2 | | 26 | MERGE JOIN CARTESIAN | | 1 | 1 |00:00:00.01| 15 | 1 | | 27 | MERGE JOIN CARTESIAN | | 1 | 1 |00:00:00.01| 9 | 0 | | 28 | MERGE JOIN CARTESIAN | | 1 | 1 |00:00:00.01| 6 | 0 | | 29 | VIEW | | 1 | 1 |00:00:00.01| 3 | 0 | | 30 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6D31_64822175 | 1 | 1 |00:00:00.01| 3 | 0 | | 31 | BUFFER SORT | | 1 | 1 |00:00:00.01| 3 | 0 | | 32 | VIEW | | 1 | 1 |00:00:00.01| 3 | 0 | | 33 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6D31_64822175 | 1 | 1 |00:00:00.01| 3 | 0 | | 34 | BUFFER SORT | | 1 | 1 |00:00:00.01| 3 | 0 | | 35 | VIEW | | 1 | 1 |00:00:00.01| 3 | 0 | | 36 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6D33_64822175 | 1 | 1 |00:00:00.01| 3 | 0 | | 37 | BUFFER SORT | | 1 | 1 |00:00:00.01| 6 | 1 | | 38 | VIEW | | 1 | 1 |00:00:00.01| 6 | 1 | | 39 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6D34_64822175 | 1 | 1 |00:00:00.01| 6 | 1 | | 40 | BUFFER SORT | | 1 | 1 |00:00:00.01| 6 | 1 | | 41 | VIEW | | 1 | 1 |00:00:00.01| 6 | 1 | | 42 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6D32_64822175 | 1 | 1 |00:00:00.01| 6 | 1 | |* 43 | INDEX RANGE SCAN | TW_DIM_TIME_N2 | 1 | 240 |00:00:00.01| 3 | 0 | | 44 | HASH GROUP BY | | 1 | 8 |00:04:19.89| 293K| 291K| |* 45 | HASH JOIN OUTER | | 1 | 11 |00:04:19.89| 293K| 291K| |* 46 | HASH JOIN OUTER | | 1 | 11 |00:04:01.98| 257K| 255K| |* 47 | HASH JOIN OUTER | | 1 | 11 |00:02:23.00| 147K| 147K| |* 48 | HASH JOIN OUTER | | 1 | 11 |00:00:51.74| 45941 | 45832 | | 49 | TABLE ACCESS BY INDEX ROWID | TW_DIM_CLUP | 1 | 11 |00:00:00.10| 103 | 23 | | 50 | NESTED LOOPS | | 1 | 23 |00:00:00.08| 96 | 20 | | 51 | MERGE JOIN CARTESIAN | | 1 | 11 |00:00:00.08| 83 | 20 | | 52 | VIEW | | 1 | 1 |00:00:00.01| 6 | 1 | | 53 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6D35_64822175 | 1 | 1 |00:00:00.01| 6 | 1 | | 54 | BUFFER SORT | | 1 | 11 |00:00:00.08| 77 | 19 | | 55 | VIEW | | 1 | 11 |00:00:00.08| 77 | 19 | |* 56 | MAT_VIEW ACCESS BY INDEX ROWID | TD_ROW_LEVEL_SECURITY_MV | 1 | 11 |00:00:00.05| 67 | 16 | | 57 | NESTED LOOPS | | 1 | 2015 |00:00:00.03| 43 | 6 | | 58 | MERGE JOIN CARTESIAN | | 1 | 11 |00:00:00.01| 16 | 0 | | 59 | MAT_VIEW ACCESS BY INDEX ROWID | TD_INDIVIDUAL_MV | 1 | 1 |00:00:00.01| 3 | 0 | |* 60 | INDEX RANGE SCAN | TD_INDIVIDUAL_N1 | 1 | 1 |00:00:00.01| 2 | 0 | | 61 | BUFFER SORT | | 1 | 11 |00:00:00.01| 13 | 0 | |* 62 | MAT_VIEW ACCESS BY INDEX ROWID| TD_CLUP_DETAIL_MV | 1 | 11 |00:00:00.01| 13 | 0 | |* 63 | INDEX RANGE SCAN | TD_CLUP_DETAIL_MV_N2 | 1 | 11 |00:00:00.01| 2 | 0 | |* 64 | INDEX RANGE SCAN | TD_ROW_LEVEL_SECURITY_MV_N2 | 11 | 2003 |00:00:00.03| 27 | 6 | |* 65 | INDEX RANGE SCAN | TW_DIM_CLUP_N5 | 11 | 11 |00:00:00.01| 13 | 0 | | 66 | VIEW | | 1 | 99592 |00:00:51.47| 45838 | 45809 | | 67 | HASH GROUP BY | | 1 | 99592 |00:00:51.47| 45838 | 45809 | | 68 | TABLE ACCESS BY INDEX ROWID | TW_FACT_CLUP_MONTHLY | 1 | 99592 |00:00:51.02| 45838 | 45725 | | 69 | NESTED LOOPS | | 1 | 99594 |00:00:00.50| 116 | 113 | | 70 | VIEW | | 1 | 1 |00:00:00.01| 3 | 0 | | 71 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6D35_64822175 | 1 | 1 |00:00:00.01| 3 | 0 | |* 72 | INDEX RANGE SCAN | TW_FACT_CLUP_EOM_TIME_SYS_N1| 1 | 99592 |00:00:00.27| 113 | 113 | | 73 | VIEW | | 1 | 111K|00:01:31.06| 101K| 101K| | 74 | HASH GROUP BY | | 1 | 111K|00:01:31.06| 101K| 101K| | 75 | TABLE ACCESS BY INDEX ROWID | TW_FACT_CLUP_MONTHLY | 1 | 331K|00:01:29.92| 101K| 101K| | 76 | NESTED LOOPS | | 1 | 331K|00:00:01.32| 576 | 373 | | 77 | NESTED LOOPS | | 1 | 91 |00:00:00.01| 26 | 2 | | 78 | VIEW | | 1 | 1 |00:00:00.01| 3 | 2 | | 79 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6D34_64822175 | 1 | 1 |00:00:00.01| 3 | 2 | |* 80 | TABLE ACCESS FULL | TW_DIM_TIME | 1 | 91 |00:00:00.01| 23 | 0 | |* 81 | INDEX RANGE SCAN | TW_FACT_CLUP_EOM_TIME_SYS_N1| 91 | 331K|00:00:00.81| 550 | 371 | | 82 | VIEW | | 1 | 112K|00:01:38.77| 109K| 108K| | 83 | HASH GROUP BY | | 1 | 112K|00:01:38.77| 109K| 108K| | 84 | TABLE ACCESS BY INDEX ROWID | TW_FACT_CLUP_MONTHLY | 1 | 335K|00:01:37.63| 109K| 108K| | 85 | NESTED LOOPS | | 1 | 335K|00:00:01.33| 588 | 375 | | 86 | NESTED LOOPS | | 1 | 95 |00:00:00.01| 26 | 2 | | 87 | VIEW | | 1 | 1 |00:00:00.01| 3 | 2 | | 88 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6D32_64822175 | 1 | 1 |00:00:00.01| 3 | 2 | |* 89 | TABLE ACCESS FULL | TW_DIM_TIME | 1 | 95 |00:00:00.01| 23 | 0 | |* 90 | INDEX RANGE SCAN | TW_FACT_CLUP_EOM_TIME_SYS_N1| 95 | 335K|00:00:00.81| 562 | 373 | | 91 | VIEW | | 1 | 112K|00:00:17.72| 35623 | 35411 | | 92 | HASH GROUP BY | | 1 | 112K|00:00:17.61| 35623 | 35411 | | 93 | TABLE ACCESS BY INDEX ROWID | TW_FACT_CLUP_MONTHLY | 1 | 112K|00:00:17.30| 35623 | 35411 | | 94 | NESTED LOOPS | | 1 | 112K|00:00:00.34| 130 | 128 | | 95 | VIEW | | 1 | 1 |00:00:00.01| 3 | 2 | | 96 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6D35_64822175 | 1 | 1 |00:00:00.01| 3 | 2 | |* 97 | INDEX RANGE SCAN | TW_FACT_CLUP_EOM_TIME_SYS_N1| 1 | 112K|00:00:00.16| 127 | 126 | ---------------------------------------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id):

Page 34: Managing SQL Performance

! ! !

!

! ! ! ! ! ! ! ! ! ! !1\!

--------------------------------------------------- 1 - filter(ROWNUM=1) 2 - filter(UPPER("CON"."POSITION_TYPE_CD")='PRINCIPAL') 3 - access("CON"."LOC_SYS_ID"=:B1) 9 - filter(("ACCT_MONTH_YEAR"<='200912' AND "ACTUAL_DATE"<SYSDATE@!)) 10 - access("IS_ACCT_END_OF_MONTH"='1') 12 - access("T3"."TIME_WH_SYS_ID"="T"."TIME_WH_SYS_ID") 23 - filter("T2"."ACCT_PERIOD_DT"=ADD_MONTHS(INTERNAL_FUNCTION("CURR_PERIOD"."ACCT_PERIOD_DT"),-12)) 43 - access("IS_ACCT_END_OF_MONTH"='1') 45 - access("CLUP"."CLUP_WH_SYS_ID"="CURR_YTD_NET_PRM"."CLUP_WH_SYS_ID") 46 - access("CLUP"."CLUP_WH_SYS_ID"="CURR_Q_NET_PRM"."CLUP_WH_SYS_ID") 47 - access("CLUP"."CLUP_WH_SYS_ID"="PRIOR_Q_NET_PRM"."CLUP_WH_SYS_ID") 48 - access("CLUP"."CLUP_WH_SYS_ID"="PRIOR_YTD_NET_PRM"."CLUP_WH_SYS_ID") 56 - filter("RL"."USER_SYS_ID"="I"."INDIV_SYS_ID") 60 - access("I"."USER_LOGIN_ID"='TraxReporting') 62 - filter(("CD"."DOC_TYPE_CD"='AGENCY' OR "CD"."DOC_TYPE_CD"='FNAS-LNR')) 63 - access("CD"."CUST_SYS_ID"=5922) 64 - access("CD"."CLUP_SYS_ID"="RL"."CLUP_SYS_ID") 65 - access("LOC_DETAILS"."CLUP_SYS_ID"="CLUP"."CLUP_SYS_ID") 72 - access("FCM"."TIME_WH_SYS_ID"="TS"."PRIOR_YR_TIME_SYS_ID") 80 - filter(("T5"."ACCT_MONTH_YEAR">="PQ"."BEGIN_PRIOR_QTR" AND "T5"."ACCT_MONTH_YEAR"<="PQ"."END_PRIOR_QTR")) 81 - access("FCM"."TIME_WH_SYS_ID"="T5"."TIME_WH_SYS_ID") 89 - filter(("T6"."ACCT_MONTH_YEAR">="CQ"."BEGIN_CURR_QTR" AND "T6"."ACCT_MONTH_YEAR"<="CQ"."END_CURR_QTR")) 90 - access("FCM"."TIME_WH_SYS_ID"="T6"."TIME_WH_SYS_ID")

97 - access("FCM"."TIME_WH_SYS_ID"="TS"."TIME_WH_SYS_ID")

:>%!&/G7(6/'!%')%)!75!?7((6';!(>%!G%';(>!/B!(>%!/$6;6'-G!&(-(%4%'(!*+!4/$%!(>-'!>-GBL!g6$(7-GG+!-GG!(>%!A/$J!(/!B6;7$%!/7(!(64%!5%$6/)&!-')!?/457(%!V-G7%&!B/$!%-?>!5%$6/)!6')6V6)7-GG+!A-&!$%5G-?%)!*+!&645G+!;$/756';!*+!(>%!+%-$!-')!X7-$(%$!_)%(%$46'%)!*+!7&6';!(/j?>-$_v)-(%!B6%G)wKUOU``!-')!7&6';!(>%!SFPPT#!/5(6/'!/B!dSFT#!YZ!(/!;%(!47G(65G%!G%V%G&!/B!(/(-G&L!

WITH /* k2rewrite */ access_lvl as (SELECT row_access_auth_cd, indiv_sys_id FROM td_individual_mv WHERE user_login_id = 'TraxReporting') , clups AS (SELECT cd.clup_sys_id FROM td_clup_detail_mv cd, access_lvl a WHERE cd.doc_type_cd IN ('AGENCY', 'FNAS-LNR') AND cd.cust_sys_id = 5922 AND a.row_access_auth_cd = 'RESTRICTED' AND cd.clup_sys_id in (select rl.clup_sys_id from td_row_level_security_mv rl where rl.user_sys_id = a.indiv_sys_id) UNION ALL SELECT cd.clup_sys_id FROM td_clup_detail_mv cd, access_lvl a WHERE cd.doc_type_cd IN ('AGENCY', 'FNAS-LNR') AND cd.cust_sys_id = 5922 AND a.row_access_auth_cd = 'UNRESTRICTED') , ans as (SELECT dtl.clup_sys_id, dt.acct_year, to_char(dt.acct_period_dt,'Q') acct_qtr, SUM(NVL (fcm.net_prm_tot_eom_amt, 0)) net_prm FROM clups dtl ,tw_dim_clup clup ,tw_fact_clup_monthly fcm ,tw_dim_time dt WHERE dtl.clup_sys_id = clup.clup_sys_id AND clup.clup_wh_sys_id = fcm.clup_wh_sys_id AND fcm.time_wh_sys_id = dt.time_wh_sys_id AND dt.acct_month_year BETWEEN '200801' AND '200912' AND dt.is_acct_end_of_month = '1' GROUP BY dtl.clup_sys_id, ROLLUP ( dt.acct_year, to_char(dt.acct_period_dt,'Q') )) , dtls as (select cd.rollup_uw_abbv_cd

Page 35: Managing SQL Performance

! ! ! ! !! !

! ! ! ! 1a!

,CASE WHEN cd.cust_loc_nm IS NOT NULL AND cd.dba_nm_from_loc_nm_ind = 1 THEN cd.cust_loc_nm WHEN cd.cust_doing_bus_as_nm IS NOT NULL THEN cd.cust_doing_bus_as_nm ELSE cd.cust_bus_nm END cust_loc_nm ,cd.loc_city_nm , cd.loc_rep_f_nm || ' ' || cd.loc_rep_mid_int_nm || ' ' || cd.loc_rep_l_nm AS loc_rep_nm , (SELECT NVL (con.first_nm, '') || ' ' || NVL (con.mid_int_nm, '') || ' ' || NVL (con.last_nm, '') || ' ' || NVL (con.sufx_nm, '') FROM td_contact_mv con WHERE con.loc_sys_id = cd.loc_sys_id AND UPPER (con.position_type_cd) = 'PRINCIPAL' AND ROWNUM = 1) principal_nm, ans.acct_year, ans.acct_qtr, ans.net_prm from ans, td_clup_detail_mv cd where ans.clup_sys_id = cd.clup_sys_id) , outlist1 as (select rollup_uw_abbv_cd ,cust_loc_nm ,loc_city_nm ,loc_rep_nm ,principal_nm ,acct_year ,acct_qtr ,sum(net_prm) as net_prm from dtls group by rollup_uw_abbv_cd, cust_loc_nm, loc_city_nm, loc_rep_nm, principal_nm,acct_year,acct_qtr) , outlist2 as (select rollup_uw_abbv_cd, cust_loc_nm, loc_city_nm, loc_rep_nm, principal_nm, curr_yr_ytd, prior_yr_ytd, curr_yr_ytd - prior_yr_ytd as ytd_variance from (select rollup_uw_abbv_cd, cust_loc_nm, loc_city_nm, loc_rep_nm, principal_nm, max( decode( acct_year, '2009', net_prm, null ) ) curr_yr_ytd, max( decode( acct_year, '2008', net_prm, null ) ) prior_yr_ytd from outlist1 where (acct_year between '2008' and '2009' and acct_qtr is null) group by rollup_uw_abbv_cd, cust_loc_nm, loc_city_nm, loc_rep_nm, principal_nm)) , outlist3 as (select rollup_uw_abbv_cd, cust_loc_nm, loc_city_nm, loc_rep_nm, principal_nm, curr_qtr_ytd - prior_qtr_ytd as qvariance from (select rollup_uw_abbv_cd, cust_loc_nm, loc_city_nm, loc_rep_nm, principal_nm, max( decode( acct_qtr, '4', net_prm, null ) ) curr_qtr_ytd, max( decode( acct_qtr, '3', net_prm, null ) ) prior_qtr_ytd from outlist1 where (acct_year = '2009' and acct_qtr between '3' and '4') group by rollup_uw_abbv_cd, cust_loc_nm, loc_city_nm, loc_rep_nm, principal_nm, acct_year)) select a.rollup_uw_abbv_cd, a.cust_loc_nm, a.loc_city_nm, a.loc_rep_nm, a.principal_nm, a.curr_yr_ytd, a.prior_yr_ytd, a.ytd_variance, b.qvariance from outlist2 a, outlist3 b where a.rollup_uw_abbv_cd = b.rollup_uw_abbv_cd and a.cust_loc_nm = b.cust_loc_nm and a.loc_city_nm = b.loc_city_nm and a.loc_rep_nm = b.loc_rep_nm and a.principal_nm = b.principal_nm order by a.cust_loc_nm, a.loc_city_nm, a.loc_rep_nm, a.principal_nm, a.rollup_uw_abbv_cd;

Page 36: Managing SQL Performance

! ! !

!

! ! ! ! ! ! ! ! ! ! !1D!

:>%!&(-(%4%'(K!A>6G%!&(6GG!G%';(>+K!A-&!47?>!&>/$(%$!-')!G%&&!?74*%$&/4%!(>-'!(>%!/$6;6'-GL!W(!-G&/!$%4/V%)!$%5%-(%)!-??%&&%&!(/!/*]%?(&!_(-J6';!?-$%!/B!/7$!Nd-(G6';!;7'Q!-'(65-((%$'!%G646'-(6/'`!$%&7G(6';!6'!-!47?>!4/$%!&($%-4G6'%)!-')!%BB6?6%'(!&(-(%4%'(!-&!%V6)%'?%)!*+!(>%!'%A!%@%?7(6/'!5G-'H!

SQL_ID b7tqyy6htag53, child number 0 ------------------------------------- Plan hash value: 1922825908 -------------------------------------------------------------------------------------------------------------------------------------- | Id | Operation | Name |Starts |A-Rows | A-Time |Buffers | Reads | -------------------------------------------------------------------------------------------------------------------------------------- | 1 | TEMP TABLE TRANSFORMATION | | 1 | 8 |00:00:07.52 | 1853 | 1348 | | 2 | LOAD AS SELECT | | 1 | 1 |00:00:00.01 | 7 | 0 | | 3 | MAT_VIEW ACCESS BY INDEX ROWID | TD_INDIVIDUAL_MV | 1 | 1 |00:00:00.01 | 3 | 0 | |* 4 | INDEX RANGE SCAN | TD_INDIVIDUAL_N1 | 1 | 1 |00:00:00.01 | 2 | 0 | | 5 | LOAD AS SELECT | | 1 | 1 |00:00:07.51 | 1831 | 1347 | | 6 | HASH GROUP BY | | 1 | 86 |00:00:07.51 | 1827 | 1347 | | 7 | VIEW | | 1 | 119 |00:00:07.51 | 1827 | 1347 | | 8 | MAT_VIEW ACCESS BY INDEX ROWID | TD_CLUP_DETAIL_MV | 1 | 119 |00:00:07.50 | 1818 | 1344 | | 9 | NESTED LOOPS | | 1 | 239 |00:00:07.50 | 1807 | 1344 | | 10 | VIEW | | 1 | 119 |00:00:07.48 | 1686 | 1342 | | 11 | SORT GROUP BY ROLLUP | | 1 | 119 |00:00:07.48 | 1686 | 1342 | |* 12 | HASH JOIN | | 1 | 258 |00:00:07.48 | 1686 | 1342 | | 13 | TABLE ACCESS BY INDEX ROWID | TW_FACT_CLUP_MONTHLY | 1 | 1607 |00:00:07.47 | 1666 | 1342 | | 14 | NESTED LOOPS | | 1 | 1619 |00:00:00.04 | 75 | 11 | | 15 | NESTED LOOPS | | 1 | 11 |00:00:00.01 | 46 | 4 | | 16 | VIEW | | 1 | 11 |00:00:00.01 | 22 | 1 | | 17 | UNION-ALL | | 1 | 11 |00:00:00.01 | 22 | 1 | | 18 | NESTED LOOPS SEMI | | 1 | 0 |00:00:00.01 | 6 | 1 | | 19 | MERGE JOIN CARTESIAN | | 1 | 0 |00:00:00.01 | 6 | 1 | |* 20 | VIEW | | 1 | 0 |00:00:00.01 | 6 | 1 | | 21 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6D36_64822175 | 1 | 1 |00:00:00.01 | 6 | 1 | | 22 | BUFFER SORT | | 0 | 0 |00:00:00.01 | 0 | 0 | |* 23 | MAT_VIEW ACCESS BY INDEX ROWID| TD_CLUP_DETAIL_MV | 0 | 0 |00:00:00.01 | 0 | 0 | |* 24 | INDEX RANGE SCAN | TD_CLUP_DETAIL_MV_N2 | 0 | 0 |00:00:00.01 | 0 | 0 | |* 25 | MAT_VIEW ACCESS BY INDEX ROWID | TD_ROW_LEVEL_SECURITY_MV | 0 | 0 |00:00:00.01 | 0 | 0 | |* 26 | INDEX RANGE SCAN | TD_ROW_LEVEL_SECURITY_MV_N2 | 0 | 0 |00:00:00.01 | 0 | 0 | | 27 | MERGE JOIN CARTESIAN | | 1 | 11 |00:00:00.01 | 16 | 0 | |* 28 | VIEW | | 1 | 1 |00:00:00.01 | 3 | 0 | | 29 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6D36_64822175 | 1 | 1 |00:00:00.01 | 3 | 0 | | 30 | BUFFER SORT | | 1 | 11 |00:00:00.01 | 13 | 0 | |* 31 | MAT_VIEW ACCESS BY INDEX ROWID | TD_CLUP_DETAIL_MV | 1 | 11 |00:00:00.01 | 13 | 0 | |* 32 | INDEX RANGE SCAN | TD_CLUP_DETAIL_MV_N2 | 1 | 11 |00:00:00.01 | 2 | 0 | | 33 | TABLE ACCESS BY INDEX ROWID | TW_DIM_CLUP | 11 | 11 |00:00:00.01 | 24 | 3 | |* 34 | INDEX RANGE SCAN | TW_DIM_CLUP_N5 | 11 | 11 |00:00:00.01 | 13 | 0 | |* 35 | INDEX RANGE SCAN | TW_FACT_CLUP_EOM_PK1 | 11 | 1607 |00:00:00.03 | 29 | 7 | |* 36 | TABLE ACCESS BY INDEX ROWID | TW_DIM_TIME | 1 | 24 |00:00:00.01 | 20 | 0 | |* 37 | INDEX RANGE SCAN | TW_DIM_TIME_N2 | 1 | 240 |00:00:00.01 | 3 | 0 | |* 38 | INDEX RANGE SCAN | TD_CLUP_DETAIL_MV_N6 | 119 | 119 |00:00:00.02 | 121 | 2 | | 39 | SORT ORDER BY | | 1 | 8 |00:00:00.01 | 9 | 1 | |* 40 | HASH JOIN | | 1 | 8 |00:00:00.01 | 9 | 1 | | 41 | VIEW | | 1 | 8 |00:00:00.01 | 6 | 1 | | 42 | HASH GROUP BY | | 1 | 8 |00:00:00.01 | 6 | 1 | |* 43 | VIEW | | 1 | 16 |00:00:00.01 | 6 | 1 | | 44 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6D37_64822175 | 1 | 86 |00:00:00.01 | 6 | 1 | | 45 | VIEW | | 1 | 8 |00:00:00.01 | 3 | 0 | | 46 | HASH GROUP BY | | 1 | 8 |00:00:00.01 | 3 | 0 | |* 47 | VIEW | | 1 | 16 |00:00:00.01 | 3 | 0 | | 48 | TABLE ACCESS FULL | SYS_TEMP_0FD9D6D37_64822175 | 1 | 86 |00:00:00.01 | 3 | 0 | -------------------------------------------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 4 - access("USER_LOGIN_ID"='TraxReporting') 12 - access("FCM"."TIME_WH_SYS_ID"="DT"."TIME_WH_SYS_ID") 20 - filter("A"."ROW_ACCESS_AUTH_CD"='RESTRICTED') 23 - filter(("CD"."DOC_TYPE_CD"='AGENCY' OR "CD"."DOC_TYPE_CD"='FNAS-LNR')) 24 - access("CD"."CUST_SYS_ID"=5922) 25 - filter("RL"."USER_SYS_ID"="A"."INDIV_SYS_ID") 26 - access("CD"."CLUP_SYS_ID"="RL"."CLUP_SYS_ID") 28 - filter("A"."ROW_ACCESS_AUTH_CD"='UNRESTRICTED') 31 - filter(("CD"."DOC_TYPE_CD"='AGENCY' OR "CD"."DOC_TYPE_CD"='FNAS-LNR')) 32 - access("CD"."CUST_SYS_ID"=5922)

Page 37: Managing SQL Performance

! ! ! ! !! !

! ! ! ! 1c!

34 - access("DTL"."CLUP_SYS_ID"="CLUP"."CLUP_SYS_ID") 35 - access("CLUP"."CLUP_WH_SYS_ID"="FCM"."CLUP_WH_SYS_ID") 36 - filter(("DT"."ACCT_MONTH_YEAR">='200801' AND "DT"."ACCT_MONTH_YEAR"<='200912')) 37 - access("DT"."IS_ACCT_END_OF_MONTH"='1') 38 - access("ANS"."CLUP_SYS_ID"="CD"."CLUP_SYS_ID") 40 - access("A"."ROLLUP_UW_ABBV_CD"="B"."ROLLUP_UW_ABBV_CD" AND "A"."CUST_LOC_NM"="B"."CUST_LOC_NM" AND "A"."LOC_CITY_NM"="B"."LOC_CITY_NM" AND "A"."LOC_REP_NM"="B"."LOC_REP_NM" AND "A"."PRINCIPAL_NM"="B"."PRINCIPAL_NM") 43 - filter(("ACCT_YEAR">='2008' AND "ACCT_YEAR"<='2009' AND "ACCT_QTR" IS NULL)) 47 - filter(("ACCT_YEAR"='2009' AND "ACCT_QTR">='3' AND "ACCT_QTR"<='4'))

i/(!/'G+!)6)!(>%!%@%?7(6/'!(64%!?/4%!)/A'!(/!7')%$!e!&%?/')&K!A%GG!7')%$!(>%!/'G6'%!(64%/7(K!*7(!(>%!*G/?J!-??%&&%&!)$/55%)!B$/4!'%-$G+!188,!)/A'!(/!]7&(!"Kea1L!:>%!*/((/4^G6'%!A6(>!(>6&!-'(65-((%$'!6&!(>-(!6B!+/7!(>6'J!&/4%(>6';!6&!]7&(!(//!?/45G%@!-')!G%';(>+K!6(!5$/*-*G+!6&L!

XBX a632&-.NN.K%#$&5,.0+D6,+M&

:>%!N]7&(!B/GG/A6';!5$/?%)7$%Q!-'(65-((%$'!/??7$&!A>%'!)%V%G/5%$&!>-V%!)6BB6?7G(+!4-J6';!(>%!G%-5!B$/4!-!5$/?%)7$-G!A-+!/B!(>6'J6';!(/!-!&%(^*-&%)!A-+!/B!(>6'J6';L!WB!+/7!(>6'J!5$/?%)7$-GG+K!+/7U$%!(>6'J6';!6'!(%$4&!/B!/'%!$/A!-(!-!(64%L!Z/7UGG!/B(%'!B6')!+/7$&%GB!5/')%$6';!>/A!(/!>-')G%!+/7$!X7%$+!B/$47G-(6/'!A6(>!5>$-&%&!G6J%!NB/$!%-?>!$/A!)/!@Q!/$!NA>6G%!V-G7%!6&!+!)/![LQ!<%V%G/56';!3OP!7&6';!(>6&!A-+!/B!(>6'J6';!?-'!/B(%'!)$6V%!?>/6?%&!(>-(!4-+!'/(!5%$B/$4!-&!A%GG!-&!-'!-G(%$'-(%!-55$/-?>!?$%-(%)!7&6';!-!&%(^*-&%)!A-+!/B!(>6'J6';L!

XBXB9 FQ"/5N+]&A./562+&"#&"I+,"$+&#6/P+,&.-&D"43&P+2K++#&.,D+,3&-.,&"&0632./+,&

W'!(>6&!%@-45G%K!(>%!(-&J!6&!(/!?/457(%!-'!-V%$-;%!'74*%$!/B!)-+&!*%(A%%'!/$)%$&!B/$!-!?7&(/4%$!_/$!G6&(!/B!?7&(/4%$&`L!I!5$/?%)7$-G!-55$/-?>!(/!A$6(6';!(>6&!&(-(%4%'(!4-+!6'V/GV%!%@%?7(6';!&%V%$-G!)6BB%$%'(!X7%$6%&!(/!>%G5!*76G)!(>%!)-(-!'%%)%)!/'%!&(%5!-(!-!(64%L!!

SQL> -- Show the list of order dates for customer 102 SQL> select customer_id, order_date 2 from orders 3 where customer_id = 102 ; CUSTOMER_ID ORDER_DATE --------------- ------------------------------- 102 19-NOV-07 03.41.54.696211 PM 102 14-SEP-07 08.53.40.223345 AM 102 29-MAR-07 01.22.40.536996 PM 102 14-SEP-06 06.03.04.763452 AM SQL> SQL> -- Determine the order_date prior to the current row's order_date SQL> select customer_id, order_date, 2 lag(order_date,1,order_date) 3 over (partition by customer_id order by order_date) 4 as prev_order_date 5 from orders 6 where customer_id = 102; CUSTOMER_ID ORDER_DATE PREV_ORDER_DATE --------------- ----------------------------- ----------------------------- 102 14-SEP-06 06.03.04.763452 AM 14-SEP-06 06.03.04.763452 AM 102 29-MAR-07 01.22.40.536996 PM 14-SEP-06 06.03.04.763452 AM 102 14-SEP-07 08.53.40.223345 AM 29-MAR-07 01.22.40.536996 PM 102 19-NOV-07 03.41.54.696211 PM 14-SEP-07 08.53.40.223345 AM SQL> SQL> -- Determine the days between each order SQL> select trunc(order_date) - trunc(prev_order_date) days_between 2 from 3 ( 4 select customer_id, order_date, 5 lag(order_date,1,order_date) 6 over (partition by customer_id order by order_date) 7 as prev_order_date 8 from orders 9 where customer_id = 102

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1!C>-5(%$!\!9!3OP!W&!I*/7(!3%(&!6'!G#(/H#".-$/IJK!xI5$%&&!08"8K!,-$%'!!./$(/'K!%(L!-GLy!>-&!4/$%!)%(-6GL!

Page 38: Managing SQL Performance

! ! !

!

! ! ! ! ! ! ! ! ! ! !1e!

10 ); DAYS_BETWEEN --------------- 0 196 169 66 SQL> SQL> -- Put it together with an AVG function to get the final answer SQL> select avg(trunc(order_date) - trunc(prev_order_date)) avg_days_between 2 from 3 ( 4 select customer_id, order_date, 5 lag(order_date,1,order_date) 6 over (partition by customer_id order by order_date) 7 as prev_order_date 8 from orders 9 where customer_id = 102 10 ); AVG_DAYS_BETWEEN ---------------- 107.75

!

:>6&!G//J&!5$%((+!%G%;-'(K!)/%&'U(!6(R!W'!(>6&!%@-45G%K!WUV%!%@%?7(%)!&%V%$-G!X7%$6%&!/'%^*+^/'%!(/!&>/A!+/7!>/A!4+!(>6'J6';!B/GG/A%)!-!&(%5^*+^&(%5!5$/?%)7$-G!-55$/-?>!(/!A$6(6';!(>%!X7%$+L!M>-(!WUV%!)/'%!6&!(/!$%-)!%-?>!/$)%$!$/A!B/$!?7&(/4%$!"80!6'!/$)%$!*+!/$)%$j)-(%!-')K!7&6';!-!PId!B7'?(6/'K!G//J!*-?J!-(!(>%!5$6/$!/$)%$!$/A!(/!;%(!6(&!/$)%$j)-(%L!F'?%!W!>-V%!*/(>!)-(%&K!(>%!)-(%!B/$!(>%!?7$$%'(!$/AU&!/$)%$!-')!(>%!)-(%!B/$!(>%!5$%V6/7&!$/AU&!/$)%$K!6(U&!-!&645G%!4-((%$!(/!&7*($-?(!(>%!(A/!(/!;%(!(>%!)-+&!*%(A%%'L!P-&(G+K!W!7&%!(>%!-V%$-;%!-;;$%;-(%!B7'?(6/'!(/!;%(!4+!B6'-G!-'&A%$L!

Z/7!?-'!(%GG!(>-(!(>6&!X7%$+!6&!*76G(!B/GG/A6';!-!V%$+!5$/?%)7$-G!-55$/-?>L!:>%!*%&(!;6V%-A-+!(/!J'/A6';!(>%!-55$/-?>!6&!(>%!A-+!W!?-'!A-GJ!(>$/7;>!&%V%$-G!)6BB%$%'(!X7%$6%&!(/!&>/A!>/A!(>%!B6'-G!$%&7G(!&%(!A-&!*76G(L!W!?/7G)!&%%!(>%!)%(-6G&!-&!W!A%'(!-G/';L!M>%'!+/7U$%!(>6'J6';!6'!&%(&K!+/7UGG!B6')!(>-(!+/7!)/'U(!$%-GG+!?-$%!-*/7(!%-?>!6')6V6)7-G!%G%4%'(L!!

W!?/7G)!>-V%!)%$6V%)!(>%!&-4%!-'&A%$!7&6';!-!&%(^*-&%)!(>6'J6';!-55$/-?>!(/!?$%-(%!(>%!B/GG/A6';!X7%$+H!

SQL> select (max(trunc(order_date)) - min(trunc(order_date))) / count(*) as avg_days_between 2 from orders 3 where customer_id = 102 ; AVG_DAYS_BETWEEN ---------------- 107.75

36'?%!(>6&!%@-45G%!7&%&!-!V%$+!&4-GG!(-*G%!_6(!/'G+!>-&!"8a!$/A&`K!(>%$%!-$%!'/!)6&?%$'-*G%!5%$B/$4-'?%!645-?(L!2/A%V%$K!6B!+/7!(-J%!(>6&!&-4%!%@-45G%!-')!($-'&G-(%!6(!(/!-!G-$;%$!)-(-&%(K!+/7!?-'!*%;6'!(/!&%%!&/4%!)6BB%$%'?%&!-&!&>/A'!6'!(>6&!?/45-$6&/'H!

SQL> select /* kmset */ (max(trunc(acct_period_month_yr_dt)) - min(trunc(acct_period_month_yr_dt))) / count(*) as avg_days_between 2 from tr_policy_h 3 where cust_sys_id = 1039 ; AVG_DAYS_BETWEEN ---------------- 1.7340471092077 SQL> @pln kmset SQL_ID 24pcka78mrtdr, child number 0 -------------------------------------

Page 39: Managing SQL Performance

! ! ! ! !! !

! ! ! ! 1m!

Plan hash value: 1921842268 ----------------------------------------------------------------------------------------------- | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | ----------------------------------------------------------------------------------------------- | 1 | SORT AGGREGATE | | 1 | 1 | 1 |00:00:00.01 | 18 | |* 2 | INDEX RANGE SCAN| TR_POLICY_H_N14 | 1 | 4013 | 2335 |00:00:00.01 | 18 | ----------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("CUST_SYS_ID"=1039) SQL> select /* kmproc */ avg(trunc(acct_period_month_yr_dt) - trunc(prev_prd_date)) avg_days_between 2 from 3 ( 4 select cust_sys_id, acct_period_month_yr_dt, 5 lag(acct_period_month_yr_dt,1,acct_period_month_yr_dt) 6 over (partition by cust_sys_id order by acct_period_month_yr_dt) 7 as prev_prd_date 8 from tr_policy_h 9 where cust_sys_id = 1039 10 ); AVG_DAYS_BETWEEN ---------------- 1.7340471092077 SQL> @pln kmproc SQL_ID af532p311fdz9, child number 0 ------------------------------------- Plan hash value: 3344845210 ------------------------------------------------------------------------------------------------------------------------------------ | Id | Operation | Name | Starts |A-Rows | A-Time |Buffers |Reads | OMem | 1Mem | Used-Mem | ------------------------------------------------------------------------------------------------------------------------------------ | 1 | SORT AGGREGATE | | 1 | 1 |00:00:06.39 | 979 | 634 | | | | | 2 | VIEW | | 1 | 2335 |00:00:06.38 | 979 | 634 | | | | | 3 | WINDOW SORT | | 1 | 2335 |00:00:06.38 | 979 | 634 | 124K| 124K| 110K (0)| | 4 | TABLE ACCESS BY INDEX ROWID| TR_POLICY_H | 1 | 2335 |00:00:06.37 | 979 | 634 | | | | |* 5 | INDEX RANGE SCAN | TR_POLICY_H_N14 | 1 | 2335 |00:00:00.01 | 18 | 0 | | | | ------------------------------------------------------------------------------------------------------------------------------------ Predicate Information (identified by operation id): --------------------------------------------------- 5 - access("CUST_SYS_ID"=1039)

I&!+/7!?-'!&%%K!(>%!G-$;%$!V/G74%!/B!)-(-!&(-$(&!(/!$%V%-G!)6BB%$%'?%&!6'!>/A!(>%!(A/!X7%$6%&!5%$B/$4L!W!)/'U(!(>6'J!(>%!5$/?%)7$-G!-55$/-?>!6&!?/45G%(%G+!N*-)QL!Y7(!W!)/!(>6'J!;%((6';!%'($%'?>%)!6'!)%V%G/56';!X7%$6%&!7&6';!(>6&!4%(>/)!?-'!G%-)!(/!B/$47G-(6';!X7%$6%&!(>-(!5%$B/$4!G%&&!%BB6?6%'(G+!(>-'!(>/&%!+/7!B/$47G-(%!7&6';!-!&%(^*-&%)!-55$/-?>L!:>%!&/G7(6/'!(/!-V/6)6';!(>%!N]7&(!B/GG/A6';!5$/?%)7$%Q!-'(65-((%$'!6&!(/!&>6B(!+/7$!(>6'J6';!(/!&(-$(!*+!&%-$?>6';!B/$!-!&%(^*-&%)!-55$/-?>!-')!4/V%!(/A-$)&!-!4/$%!5$/?%)7$-G!-55$/-?>!/'G+!A>%'!-')!(/!(>%!)%;$%%!'%%)%)L!

XB[ <&K"#2&%2&"NN&

W!/B(%'!(>6'J!/B!(>6&!-&!*%6';!(>%!G-[+!4-'U&!-'(65-((%$'!&6'?%!6(!(-J%&!4/$%!%BB/$(!(/!(+5%!6'!(>%!&5%?6B6?!?/G74'&!+/7!'%%)!V%$&7&!7&6';!(>%!?-(?>^-GG!-55$/-?>L!W!?-GG!6(!NW!A-'(!6(!-GGQ!*%?-7&%!3OP!6&!/B(%'!A$6((%'!A6(>!(>%!7')%$G+6';!6)%-!(>-(!A>6G%!W!46;>(!'/(!'%%)!(>-(!)-(-!'/AK!W!4-+!'%%)!6(!G-(%$K!&/!WUGG!]7&(!&-V%!4+&%GB!&/4%!(64%!-')!4-J%!%V%$+(>6';!-V-6G-*G%!'/AL!k$%X7%'(!/??7$$%'?%!/B!N3EPEC:!pQ!X7%$6%&K!V6%A&!(>-(!%@($-?(!-GG!?/G74'&K!/$!T#<I:E!&(-(%4%'(&!(>-(!75)-(%!%V%$+!?/G74'K!$%;-$)G%&&!/B!A>%(>%$!(>%+!'%%)!(/!*%!75)-(%)!/$!'/(K!%@>6*6(!(>6&!-'(65-((%$'L!

Page 40: Managing SQL Performance

! ! !

!

! ! ! ! ! ! ! ! ! ! !\8!

XB[B9 FQ"/5N+]&'F)FA:&b&I3B&'F)FA:&c0.N6/#&N%32d&

M>%'!X7%$+6';!-!(-*G%!_5-$(6?7G-$G+!-!G-$;%!(-*G%`!(>-(!'%%)&!(/!*%!&/$(%)!/$!%V%'!]/6'%)!(/!-'/(>%$!(-*G%!7&6';!-!2I32!bFWi!/5%$-(6/'K!&5%?6B+6';!-!?/G74'!G6&(!A6GG!$%)7?%!(>%!-4/7'(!/B!4%4/$+!'%%)%)!(/!5$/?%&&!(>%!&/$(=>-&>!V%$&7&!&5%?6B+6';!-GG!?/G74'&!*%!$%(7$'%)L!W(U&!%-&+!(/!&%%!(>%!)6BB%$%'?%!A6(>!-!&645G%!?/45-$6&/'!(%&(!7&6';!(>%&%!(A/!X7%$6%&H!

select /* km_all */ * from my_objects order by owner, object_name select /* km_only */ owner, object_name from my_objects order by owner, object_name

i/(6?%!(>-(!A>6G%!*/(>!X7%$6%&!A6GG!%@%?7(%!-!B7GG!(-*G%!&?-'K!-')!(>7&!-??%&&!(>%!&-4%!*G/?J&K!(>%!X7%$+!(>-(!&5%?6B6%&!/'G+!(A/!?/G74'&!(/!*%!$%(7$'%)!7&%&!G%&&!4%4/$+!(/!>-')G%!(>%!&/$(L!:>6&!)6BB%$%'?%!$%)7?%&!(>%!$%&5/'&%!(64%!*+!-!G6((G%!4/$%!-!&%?/')L!

SQL_ID 8qf110gcy0zf6, child number 0 ------------------------------------- select /* km_all */ * from my_objects order by owner, object_name Plan hash value: 3173709044 ------------------------------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads | OMem | 1Mem | Used-Mem | ------------------------------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | | 434K|00:00:04.41 | 5986 | 5983 | | | | | 1 | SORT ORDER BY | | 1 | 434K| 434K|00:00:04.41 | 5986 | 5983 | 57M| 2653K| 51M (0)| | 2 | TABLE ACCESS FULL| MY_OBJECTS | 1 | 434K| 434K|00:00:02.61 | 5986 | 5983 | | | | ------------------------------------------------------------------------------------------------------------------------------- SQL_ID 09rv44ga24u2u, child number 0 ------------------------------------- select /* km_only */ owner, object_name from my_objects order by owner, object_name Plan hash value: 3173709044 ------------------------------------------------------------------------------------------------------------------------------- | Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads | OMem | 1Mem | Used-Mem | ------------------------------------------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | | 434K|00:00:03.38 | 5986 | 5978 | | | | | 1 | SORT ORDER BY | | 1 | 434K| 434K|00:00:03.38 | 5986 | 5978 | 18M| 1608K| 16M (0)| | 2 | TABLE ACCESS FULL| MY_OBJECTS | 1 | 434K| 434K|00:00:01.94 | 5986 | 5978 | | | | -------------------------------------------------------------------------------------------------------------------------------

i/A!64-;6'%!(>6&!%BB%?(!4-;'6B6%)!-?$/&&!G-$;%$!)-(-&%(&L!S%&5/'&%!(64%&!?/7G)!*%!6'?$%-&%)!*+!&%?/')&!/$!46'7(%&!)%5%')6';!/'!(>%!*+(%&!/B!)-(-!B$/4!?/G74'&!(>-(!7G(64-(%G+!A/'U(!*%!6'?G7)%)!6'!(>%!B6'-G!$%&7G(!&%(L!M>+!A-&(%!(>%!(64%!-')!$%&/7$?%&!(/!/'G+!(>$/A!-A-+!(>%!)-(-!G-(%$R!!

:>6&!-'(65-((%$'!)/%&'U(!]7&(!&>/A!75!6'!(>%!7&%!/B!N3EPEC:!pQ!*7(!+/7UGG!B$%X7%'(G+!&%%!6(!6'!(>%!?$%-(6/'!-')!7&%!/B!N/'%!&6[%!B6(&!-GGQ!V6%A&L!I!V6%A!A6GG!*%!?$%-(%)!(>-(!?/'(-6'&!'74%$/7&!?/G74'&!(>-(!X7%$6%&!-;-6'&(!(>%!V6%A!'%V%$!$%X7%&(L!Y7(K!(>%!V6%A!?/'(-6'&!(>/&%!?/G74'&!8@3)/,%/."3$!&/4%/'%!46;>(!%V%$!'%%)!(>%4L!W!-)V/?-(%!-;-6'&(!(>6&!-55$/-?>!-')!6'&(%-)!5$%B%$!(/!&%%!V6%A&!*%!4/)6B6%)!(/!-))!?/G74'&!6B=A>%'!'%?%&&-$+!-')!'/(!7'(6G!(>-(!'%%)!4-+!-$6&%L!

XB[B7 FQ"/5N+]&@*;C:F'&2H"2&3+2&Q&e&Q&

:>%!/(>%$!?/44/'!/??7$$%'?%!/B!(>%!NW!A-'(!6(!-GGQ!-'(65-((%$'!6&!B/7')!6'!T#<I:E!&(-(%4%'(&!A>%$%!V-G7%&!-$%!&%(!B/$!?/G74'&!A>%'!(>%!?/G74'!V-G7%&!-$%'U(!?>-';%)L!:>%!B/GG/A6';!&645G%!%@-45G%!)%4/'&($-(%&!(>%!5%$B/$4-'?%!5$/*G%4!(>6&!?-'!?-7&%L!:>%!(%&(!(-*G%!>-&!/'G+!1!?/G74'&!_C"K!C0K!C1`!-')!/'%!46GG6/'!$/A&L!EV%$+!$/A!>-&!-!V-G7%!/B!"!B/$!C"!*7(!(>%$%!-$%!/'G+!"88!$/A&!A>%$%!C0!z!"L!!

SQL> @ds Table Owner : hr

Page 41: Managing SQL Performance

! ! ! ! !! !

! ! ! ! \"!

Table Name : redo_t Column List : c1,c2 Where Clause : Page Size[30]: Table blocks below hwm Table rows (B) (R) ---------------------- ---------------- 16,256 1,000,000 Block selectivity Block count Row selectivity Row count C1 C2 (pb = b/B) (b) (pr = r/R) (r) --------------- --------------- ----------------- -------------- ----------------- ---------------- 1 2 96.11% 15,624 99.99% 999,900 1 1 0.01% 2 0.01% 100 SQL> select n.name, t.value 2 from v$mystat t, v$statname n 3 where t.statistic# = n.statistic# 4 and n.name = 'redo size'; NAME VALUE --------------- ---------- redo size 784 SQL> update /* upd1 */ redo_t 2 set c2 = 2 3 where c1 = 1; 1000000 rows updated. SQL> select n.name, t.value 2 from v$mystat t, v$statname n 3 where t.statistic# = n.statistic# 4 and n.name = 'redo size'; NAME VALUE -------------- ---------- redo size 294319144 SQL> @pln upd1 SQL_ID 3vck7cgqa34us, child number 0 ------------------------------------- update /* upd1 */ redo_t set c2 = 2 where c1 = 1 Plan hash value: 2614511178 ------------------------------------------------------------------------------------------------------------ | Id | Operation | Name | Starts | E-Rows |E-Bytes| Cost (%CPU)| A-Rows | A-Time | Buffers | ------------------------------------------------------------------------------------------------------------ | 0 | UPDATE STATEMENT | | 3 | | | 4376 (100)| 0 |00:00:44.12 | 1444K| | 1 | UPDATE | REDO_T | 3 | | | | 0 |00:00:44.12 | 1444K| |* 2 | TABLE ACCESS FULL| REDO_T | 3 | 1000K| 5859K| 4376 (1)| 2009K|00:00:04.54 | 400K| ------------------------------------------------------------------------------------------------------------ Predicate Information (identified by operation id): --------------------------------------------------- 2 - filter("C1"=1) SQL> select n.name, t.value 2 from v$mystat t, v$statname n 3 where t.statistic# = n.statistic# 4 and n.name = 'redo size'; NAME VALUE --------------- ---------- redo size 784 SQL> update /* upd2 */ redo_t 2 set c2 = 2 3 where c1 = 1 4 and c2 != 2; 100 rows updated. SQL> select n.name, t.value

Page 42: Managing SQL Performance

! ! !

!

! ! ! ! ! ! ! ! ! ! !\0!

2 from v$mystat t, v$statname n 3 where t.statistic# = n.statistic# 4 and n.name = 'redo size'; NAME VALUE -------------- ---------- redo size 784 SQL> @pln upd2 SQL_ID fzw4mdkxr7565, child number 0 ------------------------------------- update /* upd2 */ redo_t set c2 = 2 where c1 = 1 and c2 != 2 Plan hash value: 2614511178 ------------------------------------------------------------------------------------------------------------ | Id | Operation | Name | Starts | E-Rows |E-Bytes| Cost (%CPU)| A-Rows | A-Time | Buffers | ------------------------------------------------------------------------------------------------------------ | 0 | UPDATE STATEMENT | | 1 | | | 4377 (100)| 0 |00:00:00.11 | 15853 | | 1 | UPDATE | REDO_T | 1 | | | | 0 |00:00:00.11 | 15853 | |* 2 | TABLE ACCESS FULL| REDO_T | 1 | 500K| 2929K| 4377 (1)| 100 |00:00:00.11 | 15849 | ------------------------------------------------------------------------------------------------------------ Predicate Information (identified by operation id): --------------------------------------------------- 2 - filter(("C2"<>2 AND "C1"=1))

! :>%!4-6'!5$/*G%4!>%$%!6&!6'!(>%!;%'%$-(6/'!/B!%@?%&&!$%)/L!W'!(>6&!&645G%!?-&%K!0m\!.Y!/B!$%)/!A-&!;%'%$-(%)L!:>%!(64%!(/!?/45G%(%!(>%!T#<I:E!A-&!?/'&74%)!-G4/&(!%'(6$%G+!*+!>-V6';!(/!>-')G%!(>%!$%)/L!Y7(!(>%!)-(-!/'G+!>-)!"88!$/A&!(>-(!'%%)%)!(/!*%!?>-';%)L!Y+!&645G+!-))6';!-!?>%?J!(/!-V/6)!75)-(6';!$/A&!A>%$%!(>%!?/G74'!V-G7%!6&!-G$%-)+!&%(!(/!(>%!V-G7%!+/7!'%%)K!+/7!-V/6)!75)-(6';!6'B/$4-(6/'!(>-(!)/%&'U(!'%%)!(/!*%L!:>6&!6&!-!?G-&&6?!5$//B!(/!)%(%$46'%!(>%!B-&(%&(!A-+!(/!)/!&/4%(>6';!6&!(/!'/(!)/!6(!-(!-GGL!

XBf 'N.K&P4&3N.K&

W!*/$$/A%)!:/4!,+(%U&!5>$-&%!(/!'-4%!(>6&!-'(65-((%$'!-&!N&G/A!*+!&G/AQ!6&!(+56?-GG+!(>%!5%$B/$4-'?%!B//(5$6'(!B/$!3OP!(>-(!6&!?/)%)!7&6';!(>6&!-'(65-((%$'L!:>%$%!-$%!(A/!?-&%&!(>-(!WUGG!$%V6%A!(>-(!)%4/'&($-(%!(>6&!-'(65-((%$'!-')!6(&!%BB%?(&L!

XBfB9 FQ"/5N+]&;!)&%#3%D+&N..5&5,.0+33%#$&,.K3&.#+&"2&"&2%/+&&

:>6&!6&!(>%!?G-&&6?!%@-45G%!/B!$/A^*+^$/A!5$/?%&&6';!-')!>-&!*%%'!5$%-?>%)!%'/7;>!(>-(!4/&(!5%/5G%!J'/A!(/!-V/6)!6(K!*7(!6(!'%V%$!>7$(&!(/!$%V6%A!6(!-;-6'L!:>6&!-'(65-((%$'!&>/A&!75!A>%'!(>%$%!6&!-!'%%)!(/!)/!&/4%!6'(%$4%)6-(%!5$/?%&&6';!/'!$/A&!/B!)-(-!(>-(!6&!(//!?/45G%@!(/!*%!>-')G%)!*+!-!&6';G%!3OP!&(-(%4%'(L!36'?%!%-?>!$/A!6&!*%6';!$%V6%A%)!-')!?>-';%)!6')%5%')%'(G+K!(>%!(%45(-(6/'!6&!(/!?/45G%(%!A/$J!/'!(>-(!$/A!*+!%@%?7(6';!(>%!$%X76$%)!75)-(%!644%)6-(%G+K!(>%'!5$/?%%)!(/!(>%!'%@(!$/AL!Y7(K!-&!(>6&!%@-45G%!&>/A&K!)/6';!&/!A6GG!-))!&6;'6B6?-'(!5%$B/$4-'?%!/V%$>%-)L!

SQL> alter session set tracefile_identifier = 'FOR_LOOP'; Session altered. SQL> exec dbms_monitor.session_trace_enable; PL/SQL procedure successfully completed. SQL> SQL> DECLARE 2 TYPE NumTab IS TABLE OF widgets.widget_id%TYPE INDEX BY BINARY_INTEGER; 3 TYPE NameTab IS TABLE OF widgets.widget_nm%TYPE INDEX BY BINARY_INTEGER; 4 5 w_ids NumTab; 6 w_nms NameTab;

Page 43: Managing SQL Performance

! ! ! ! !! !

! ! ! ! \1!

7 8 ctr INTEGER := 100000 ; 9 10 BEGIN 11 FOR idx IN 1..ctr LOOP 12 w_ids(idx) := idx; 13 w_nms(idx) := 'Widget ' || TO_CHAR(idx); 14 END LOOP; 15 16 FOR idx IN 1..ctr LOOP 17 INSERT INTO widgets VALUES (w_ids(idx), w_nms(idx)); 18 END LOOP; 19 20 END; 21 / PL/SQL procedure successfully completed. Elapsed: 00:00:21.71

!

SQL> alter session set tracefile_identifier = 'FORALL'; Session altered. SQL> exec dbms_monitor.session_trace_enable; PL/SQL procedure successfully completed. SQL> SQL> SQL> DECLARE 2 TYPE NumTab IS TABLE OF widgets.widget_id%TYPE INDEX BY BINARY_INTEGER; 3 TYPE NameTab IS TABLE OF widgets.widget_nm%TYPE INDEX BY BINARY_INTEGER; 4 5 w_ids NumTab; 6 w_nms NameTab; 7 8 ctr INTEGER := 100000 ; 9 10 BEGIN 11 FOR idx IN 1..ctr LOOP 12 w_ids(idx) := idx; 13 w_nms(idx) := 'Widget ' || TO_CHAR(idx); 14 END LOOP; 15 16 FORALL idx IN w_ids.FIRST .. w_ids.LAST 17 INSERT INTO widgets VALUES (w_ids(idx), w_nms(idx)); 18 19 20 END; 21 / PL/SQL procedure successfully completed. Elapsed: 00:00:00.40

!

Page 44: Managing SQL Performance

! ! !

!

! ! ! ! ! ! ! ! ! ! !\\!

!!

! W'!(>6&!%@-45G%K!5$/?%&&6';!"88K888!Wi3ES:&!6')6V6)7-GG+!V%$&7&!5$/?%&&6';!(>%4!6'!*7GJ!7&6';!-!kFSIPP!6'?$%-&%&!(>$/7;>57(!B$/4!-55$/@64-(%G+!aK188!$/A&!5%$!&%?/')!(/!/V%$!a1cK888!$/A&!5%$!&%?/')f!S%&5/'&%!(64%!)$/55%)!B$/4!"eLeD!&%?/')&!(/!8L"eD!&%?/')&L!S/A^*+^$/A!6&!1$>,%,)$-A!&G/A!*+!&G/Af!

XBfB7 FQ"/5N+]&@3%#$&*)^'()&2.&D.&KH"2&"&3%#$N+&'()&32"2+/+#2&0.6ND&D.&

M>%'!(>%!-'(65-((%$'!-55%-$&!6'!(>6&!B/$4K!6(!6&!(+56?-GG+!$//(%)!6'!(>%!)%V%G/5%$U&!5$/?%)7$-G!A-+!/B!(>6'J6';!&/!6(!6&!-'!/BB&>//(!/B!(>%!N]7&(!B/GG/A6';!5$/?%)7$%Q!-'(65-((%$'!-&!A%GGL!:>%!4-6'!)6BB%$%'?%!*%(A%%'!(>6&!B/$4!/B!(>%!-'(65-((%$'!-')!(>%!$/A^*+^$/A!5$/?%&&6';!&>/A'!6'!(>%!5$%V6/7&!%@-45G%!6&!(>-(!(>6&!B/$4!(+56?-GG+!)/%&'U(!)/!-'+!&5%?6-G!/$!?/45G%@!4-'657G-(6/'!/B!(>%!$/A&!6(!5$/?%&&%&L!!

! W'!(>6&!%@-45G%K!A%U$%!$%V6%A6';!&/4%!?/)%!(>-(!(>%!)%V%G/54%'(!(%-4!-(!M-G4-$(!_(>%+!>-V%!/V%$!0!46GG6/'!%45G/+%%&!A/$G)A6)%`!?$%-(%)!(/!&%(!-'!-''7-G!*/'7&!-4/7'(!B/$!%-?>!%45G/+%%L!:>%!(-*G%!*%6';!75)-(%)!?/'(-6'&!-!$/A!B/$!%-?>!%45G/+%%!(>-(!$%?%6V%)!-'!-''7-G!*/'7&!G-&(!+%-$L!Y7(K!6B!-'!%45G/+%%!A-&!>6$%)!&6'?%!G-&(!+%-$U&!*/'7&%&!A%$%!5-6)K!-!$/A!B/$!(>%4!A6GG!'%%)!(/!*%!6'&%$(%)L!IGG!%45G/+%%&!A6GG!$%?%6V%!-!*/'7&!%X76V-G%'(!(/!1n!/B!(>%6$!-''7-G!&-G-$+L!:>%!)%V%G/5%$!A$/(%!(>%!B/GG/A6';!5$/?%)7$%H!

declare v_ct number := 0 ; cursor emp_cur is select emp_id, salary from wal_emps where status = 'ACTIVE'; begin for rec in emp_cur loop begin select count(emp_id) into v_ct from annual_bonus where emp_id = rec.emp_id; if v_ct > 0 then update annual_bonus set bonus_amt = rec.salary * .03 where emp_id = rec.emp_id ; else insert into annual_bonus values (rec.emp_id, rec.salary * .03); end if; exception when others then dbms_output.put_line (sqlerrm) ; end ; end loop ; end ; /

Page 45: Managing SQL Performance

! ! ! ! !! !

! ! ! ! \a!

! M>%'!%@%?7(%)K!7&6';!-!"8K888!$/A!&7*&%(K!(>6&!6&!(>%!$%&5/'&%!(64%!5$/B6G%H!

!

! :/!%@%?7(%!(>6&!5$/?%&&!B/$!"8K888!$/A&!6(!(//J!-G4/&(!e!&%?/')&L!:>-(!4-+!'/(!&%%4!(>-(!*-)!-(!B6$&(!;G-'?%K!*7(!A>-(U&!;/6';!(/!>-55%'!A>%'!6(!$7'&!B/$!-GG!0!46GG6/'!%45G/+%%&R!i/(!/'G+!)/%&!(>6&!?/)%!*G/?J!%@>6*6(!(>%!N&G/A!*+!&G/AQ!-'(65-((%$'K!6(U&!]7&(!5G-6'!-AB7G!B/$!&%V%$-G!$%-&/'&L!:>%!B6$&(!(>6';!(>-(!>-55%'&!6&!-!?/7'(!X7%$+K!A>/&%!57$5/&%!6&!(/!)%(%$46'%!6B!(>%!%45G/+%%!>-&!-!$/A!-G$%-)+!%@6&(6';!6'!(>%!-''7-Gj*/'7&!(-*G%K!6&!%@%?7(%)L!WB!(>-(!6&!($7%K!-'!T#<I:E!6&!%@%?7(%)K!/(>%$A6&%!-'!Wi3ES:!6&!%@%?7(%)L!3/K!'/(!/'G+!6&!%-?>!T#<I:E!/$!Wi3ES:!*%6';!%@%?7(%)!/'%!-(!-!(64%K!-!3EPEC:!&(-(%4%'(!6&!%@%?7(%)!(/!)%(%$46'%!A>6?>!&(-(%4%'(!&>/7G)!*%!%@%?7(%)L!

! :>6&!3EPEC:!?/7'(_p`!X7%$+!G6J%G+!)%&%$V%&!-'!-'(65-((%$'!-GG!6(&!/A'!_G%(U&!?-GG!6(!(>%!N)/A'!B/$!(>%!?/7'(Q!-'(65-((%$'`f!W(!6&!?/45G%(%G+!7''%?%&&-$+!-')!&>/7G)!*%!$%4/V%)L!W(U&!7''%?%&&-$+!&6'?%!'/!4-((%$!A>6?>!&(-(%4%'(!+/7!%@%?7(%!B6$&(K!6B!6(!B-6G&!(/!%@%?7(%!*-&%)!/'!%6(>%$!-!<T#jgIPjFijWi<s!%$$/$!6B!(>%!Wi3ES:!($6%&!(/!6'&%$(!-!$/A!(>-(!-G$%-)+!%@6&(&K!/$!-!iFj<I:IjkFTi<!%$$/$!6'!(>%!?-&%!/B!(>%!T#<I:EK!(>%!%@?%5(6/'!>-')G%$!?/7G)!?-(?>!(>%!%$$/$!-')!%@%?7(%!(>%!/(>%$!&(-(%4%'(!5$/5%$G+L!

! I!&645G%!-G(%$'-(6V%!(>-(!-!)%V%G/5%$!46;>(!(>6'J!/B!6&!(/!7&%!-!.ESdE!&(-(%4%'(L!:>%!.ESdE!A/7G)!*%!-!&6';G%!&(-(%4%'(!(>-(!A/7G)!5$/5%$G+!>-')G%!(>%!Wi3ES:&!-')!T#<I:E&!-55$/5$6-(%G+L!!

SQL> MERGE INTO annual_bonus B 2 USING ( 3 SELECT emp_id, salary 4 FROM wal_emps 5 WHERE status = 'ACTIVE') E 6 ON (B.emp_id = E.emp_id) 7 WHEN MATCHED THEN 8 UPDATE SET B.bonus_amt = E.salary * 0.03 9 WHEN NOT MATCHED THEN 10 INSERT (B.emp_id, B.bonus_amt) 11 VALUES (E.emp_id, E.salary * 0.03) 12 / 10000 rows merged.

Page 46: Managing SQL Performance

! ! !

!

! ! ! ! ! ! ! ! ! ! !\D!

!

! Y+!&645G+!?/'V%$(6';!(>%!5$/?%)7$-GK!$/A^*+^$/A!5$/?%&&6';!(/!-!&6';G%!.ESdE!&(-(%4%'(K!(>%!$%&5/'&%!(64%!)$/55%)!B$/4!cLc"m!&%?/')&!(/!"LDc1!&%?/')&L!i/(!*-)L!Y7(K!6B!(>%+!(//J!-!G//J!-(!(>%6$!$%X76$%4%'(&!-')!&(/55%)!(>6'J6';!G6'%-$G+K!(>%+!?/7G)UV%!($6%)!(>6&!6'&(%-)H!

SQL> create table annual_bonus_2011 as 2 select emp_id, salary * .03 as bonus_amt 3 from wal_emps 4 where status = 'ACTIVE'; Table created.

:>%!$%&5/'&%!(64%!5$/B6G%!A/7G)!G//J!G6J%!(>6&H!

!

! :>%!?>/6?%!(/!?$%-(%!-!*$-')!'%A!(-*G%!-')!*76G)!6(!)6$%?(G+!7&6';!-!CSEI:E!:IYPE!I3!A/7G)!;6V%!(>%4!(>%!$%&7G(!(>%+!'%%)!A6(>/7(!(>%!-))6(6/'-G!/V%$>%-)!(>-(!75)-(6';!(>%!%@6&(6';!(-*G%!$%X76$%&L!!

! I&!WUV%!-G$%-)+!4%'(6/'%)!5$%V6/7&G+K!/'%!/B!(>%!J%+&!(/!-V/6)6';!(>6&!-'(65-((%$'!6&!(/!;%(!-A-+!B$/4!(>%!>-*6(!/B!(>6'J6';!5$/?%)7$-GG+L!:>%!4/$%!+/7!?-'!(>6'J!6'!(%$4&!/B!&%(&!-')!A-+&!(/!-??/45G6&>!(-&J&!6'!(>%!B%A%&(!&(%5&!5/&&6*G%K!(>%!%-&6%$!6(!A6GG!*%?/4%!(/!-V/6)!N&G/A!*+!&G/AQ!5$/?%&&6';L!

XBg C#2%5"22+,#&>+0"5&

W!>-V%'U(!?/V%$%)!%V%$+!-'(65-((%$'!(>-(U&!/7(!(>%$%K!*7(!(>%&%!-$%!(>%!(/5!5$/*G%4!4-J%$&!W!&%%4!(/!&%%!4/&(!B$%X7%'(G+L!F'%!J%+!(/!B%$$%(6';!/7(!-'+!-'(65-((%$'!6&!(/!&(%5!-A-+!B$/4!(>%!?/)%!-&!6(!6&!A$6((%'!-')!?/'&6)%$!>/A!+/7!A/7G)!-55$/-?>!(>%!&/G7(6/'!-'%AL!W(!6&!-!>-*6(K!-')!-!*-)!/'%!-(!(>-(K!(/!(-J%!-!5//$G+!5%$B/$46';!3OP!&(-(%4%'(!-')!&(-$(!($+6';!(/!(A%-J!6(!$-(>%$!(>-'!(/!$%A$6(%!6(L!W(!6&!A-+!(//!%-&+!(/!B-GG!6'(/!(>%!($-5!/B!

Page 47: Managing SQL Performance

! ! ! ! !! !

! ! ! ! \c!

G%((6';!/G)!B-46G6-$!A-+&!/B!)/6';!(>6';&!*G6')!+/7!(/!(>%!>6))%'!($/7*G%&!A6(>6'!(>/&%!/G)!A-+&L!:-J6';!(>%!(64%!(/!%V-G7-(%!3OP!B$/4!-!B$%&>!5%$&5%?(6V%!?-'!;6V%!+/7$!46')!(>%!?>-'?%!(/!4/$%!$%-)6G+!&%%!'%A!&/G7(6/'&!6'&(%-)!/B!/G)!5$/*G%4&L!

Page 48: Managing SQL Performance

! ! !

!

! ! ! ! ! ! ! ! ! ! !\e!

[ CU?@:&:1F&C@:1?>&

,-$%'!./$(/'!6&!-!3$L!<YI!#%$B/$4-'?%!{!:7'6';!35%?6-G6&(!B/$!k6)%G6(+!W'B/$4-(6/'!3%$V6?%&L!#$6/$!(/!>%$!?7$$%'(!5/&6(6/'!&>%!A-&!-!?/'&7G(-'(!-')!%)7?-(/$!&5%?6-G6[6';!6'!-55G6?-(6/'!/5(646[-(6/'!6'!*/(>!&>/7G)%$^(/^&>/7G)%$!?/'&7G(6';!%';-;%4%'(&!-')!?G-&&$//4!&%((6';&L!k/$!/V%$!08!+%-$&K!,-$%'!>-&!A/$J%)!6'!6'B/$4-(6/'!(%?>'/G/;+!&(-$(6';!/7(!-&!-!4-6'B$-4%!5$/;$-44%$K!)%V%G/5%$K!<YIK!)-(-!-$?>6(%?(K!$%&%-$?>%$K!%)7?-(/$!-')!?/'&7G(-'(L!2-V6';!7&%)!F$-?G%!&6'?%!(>%!%-$G+!m8u&K!&>%!*%;-'!(%-?>6';!/(>%$&!>/A!(/!7&%!F$-?G%!/V%$!-!)%?-)%!-;/L!

3>%!6&!-'!-7(>/$!/B!(>$%%!F$-?G%!*//J&!B$/4!I5$%&&K!-!B$%X7%'(!&5%-J%$!-(!?/'B%$%'?%&!-')!7&%$!;$/75&K!-'!F$-?G%!ICEK!-')!-!4%4*%$!/B!(>%!F-J:-*G%!'%(A/$J!_-'!6'B/$4-G!-&&/?6-(6/'!/B!qF$-?G%!&?6%'(6&(&q!(>-(!-$%!A%GG!J'/A'!(>$/7;>/7(!(>%!F$-?G%!?/447'6(+`L!!3>%!*G/;&!-(!>((5H==J-$%'4/$(/'L*G/;&5/(L?/4L!

!!

Page 49: Managing SQL Performance

! ! ! ! !! !

! ! ! ! !

!

!

I55%')6@!I!

!

C>-5(%$!D!B$/4!!

E@5%$(!F$-?G%!#$-?(6?%&H!<-(-*-&%!I)46'6&($-(6/'!B$/4!(>%!F-J!:-*G%!

I5$%&&K!08"8!

!

Page 50: Managing SQL Performance

! ! !

!

! ! ! ! ! ! ! ! ! ! !

Page 51: Managing SQL Performance

C H A P T E R 7

■ ■ ■

1

Managing SQL Performance

What is the first thing you think of when you see the topic “Managing SQL Performance”? Do you think of response time? Do you think of user complaints about the application running “too slow”? Do you think of AWR or ADDM reports?

As far as users are concerned, performance is response time. Users don’t care about server and database configurations, network bandwidth, I/O rates, or query execution plans. They care about how fast they perceive their applications run. All that other stuff is geek speak and doesn’t even blip on their radar. Regardless of whether or not all your monitoring gadgets flash green lights of perfection, if your users are complaining, you’ve got a problem. The truth is, the seeds of those problems very likely were planted when the code was first written.

Adopting a Performance Mindset Managing the performance of your application SQL doesn’t start when your users begin to complain. It starts before the first statement is ever written. It starts when the business tasks that your application will need to service are defined. On a time line, that starting point and the first user complaint about performance could be quite far apart. But I absolutely believe that you have to start by considering your user’s experience.

If you start by thinking of how your user will experience your application, this implies that managing SQL performance is first about a mindset, not a dataset. Your mindset is, in part, related to the set of rules you’ve internalized. But it’s also about your beliefs and feelings related to what performance is and means. Do you think managing performance is hard? Do you think managing performance is, or isn’t, your responsibility? Do you think performance is something to think about later, when, or if, problems arise? Do you think managing performance is about avoiding catastrophes or about envisioning possibilities?

Sometimes your mindset is influenced by your job description. If you have not specifically been tasked with performance as a concern, you may likely ignore, or at the very least minimize, your role in ensuring optimal performance of the SQL you write. But regardless of your defined job role, I do believe that effective SQL performance management starts with your mindset. It’s how you view your role and the contributions you make in regard to the performance of your code that makes the difference between an optimally performing application and a poorly performing one.

Page 52: Managing SQL Performance

CHAPTER 7 ■ MANAGING SQL PERFORMANCE

2

Consider the definition of the word manage:

1: to handle or direct with a degree of skill: as a: to make and keep compliant b: to treat with care: husband c: to exercise executive, administrative, and supervisory direction of 2: to work upon or try to alter for a purpose 3: to succeed in accomplishing: contrive

—Merriam-Webster Online www.merriam-webster.com/dictionary/manage

This definition indicates that if you want something to be manageable, you must give it skilled attention and effort. So, first and foremost, I think managing performance is about integrating one simple principle into your mindset: I am responsible for the performance of the code I write or maintain. Without a conscious personal choice to accept responsibility for it, performance will not be manageable.

To manage performance, you first need to know how and why Oracle determines the plan operations for each query. Then you need to be able to easily and accurately capture diagnostics indicating what your application code is doing as it executes. That means learning how Oracle’s cost-based optimizer works, particularly how it utilizes statistics. And it means understanding the importance of having your application well instrumented.

The statistics used by the optimizer are like fuel for your car. The quality of the fuel you put into your vehicle affects how well your car handles, how much gas mileage it gets, how often it needs maintenance, and even how long your vehicle will be serviceable. Understanding what goes in to the optimizer so that it can choose which SQL execution plan operations are best helps you know what should reasonably be expected to occur. And if you don’t get the results you expect or get the performance you need, you can adjust the fuel.

After you understand what goes in to the optimizer so it can make the best plan choices, you then need to be able to capture diagnostics quickly and accurately. There are many ways to capture diagnostic data, but managing performance well requires that you be able to easily collect the metrics you need, when you need them. The best way to do this is to properly instrument your code. Instrumentation is just a few extra lines of code you add to your application to enable you to identify the tasks it executes (that is, SQL related to business tasks) so they are easy to find and monitor.

■ Note Statistics and instrumentation are not covered here, but the “Further Reading” section at the end of this chapter lists several sources for more information.

Hopefully, I’ve established so far that managing SQL performance starts with an attitude, a mindset. You accept responsibility for the performance of every statement you write or maintain. You build foundation knowledge about how the optimizer works and use that knowledge to feed the optimizer with quality statistics and quality code. You make your application easy to monitor by adding instrumentation that will help you get the right performance metrics when you need them. The bottom line is that your mindset is geared toward managing performance every day and not just when problems arise.

Page 53: Managing SQL Performance

CHAPTER 7 ■ MANAGING SQL PERFORMANCE

3

Defining and Measuring Performance Now that we’ve established the mindset, let’s talk about the dataset. How do you know whether the SQL you write performs fast enough to be acceptable? My favorite, simple definition of performance is this:

Fast now. Fast later.

—Cary Millsap

Performance is primarily related to time; how long does your SQL take to execute? But it is also related to what your SQL does; what resources must be acquired to obtain the desired result? Performance is measurable, but often the problem is measuring the right thing.

There are many ways to collect performance data. The key to managing performance efficiently is in knowing which ways provide you with the most accurate information possible with the least amount of time and effort. Then, after you get the information, you need to know how to respond to it.

Let’s say you are asked to write a SQL statement to retrieve aggregate totals of customer order data. You have a system-level agreement (SLA) stating that the query must execute in 4 seconds or less 95 percent of the time. You write the query and measure how long it takes to execute by using your wristwatch’s timer; the result is 3 seconds. Would you feel comfortable turning this code over to production?

I’ve seen this method in use—really, I have. It’s a bit scary, isn’t it? The measurement accuracy of your trusty timepiece is only one reason this method scares me. As I mentioned, performance is a matter of measuring time, but it’s also a matter of resource use and scalability. I don’t think your watch can effectively help you measure that. Although this is perhaps an extremely poor example of how performance could be measured, there are other commonly used methods that are almost as bad.

In the upcoming sections, I’ll review several methods for measuring performance and how each method helps you manage, or not, your SQL’s performance.

EXPLAIN PLAN The most often used method to review the performance characteristics of a query is the EXPLAIN PLAN statement. EXPLAIN PLAN displays the series of operations Oracle performs in order to run your SQL statement. It provides information on the estimates of rows to be returned, the order of access and join methods, filter operations, and aggregations, as well as optimization information such as cost and estimated time to complete.

However, there is one main problem with relying on EXPLAIN PLAN. The output lists what is supposed to happen when the statement executes, not what actually does happen. In Listing 7-1, note how I execute the first statement, and then check the shared pool for the actual execution plan by using DBMS_XPLAN.DISPLAY_CURSOR. Next I execute the same statement using EXPLAIN PLAN and display the plan using DBMS_XPLAN.DISPLAY. The actual plan and the EXPLAIN PLAN do not match.

Listing 7-1. Comparing Plan Output of EXPLAIN PLAN and DBMS_XPLAN

SQL>variable orgid number SQL>exec :orgid := 1; PL/SQL procedure successfully completed.

Page 54: Managing SQL Performance

CHAPTER 7 ■ MANAGING SQL PERFORMANCE

4

SQL>set serveroutput off SQL>SELECT AVG(BILL_SEQUENCE_ID) FROM BOM WHERE ORG_ID = :orgid ; AVG(BILL_SEQUENCE_ID) --------------------- 1 row selected. SQL>select * from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST 2 +PEEKED_BINDS -ROWS')); PLAN_TABLE_OUTPUT ------------------------------------- SQL_ID 8xbvq97cr6zx2, child number 0 ------------------------------------- SELECT AVG(BILL_SEQUENCE_ID) FROM BOM WHERE ORG_ID = :orgid Plan hash value: 1633877697 ------------------------------------------------------------------------------------ | Id | Operation | Name |Starts | A-Rows | A-Time | Buffers | ------------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | | 1 |00:00.01 | 31 | | 1 | SORT AGGREGATE | | 1 | 1 |00:00.01 | 31 | | 2 | TABLE ACCESS BY INDEX ROWID| BOM | 1 | 0 |00:00.01 | 31 | |* 3 | INDEX SKIP SCAN | BOM_N1 | 1 | 0 |00:00.01 | 31 | ------------------------------------------------------------------------------------ Peeked Binds (identified by position): -------------------------------------- 1 - (NUMBER): 1 Predicate Information (identified by operation id): --------------------------------------------------- 3 - access("ORG_ID"=:ORGID) filter("ORG_ID"=:ORGID) 26 rows selected. SQL> SQL>explain plan for SELECT AVG(BILL_SEQUENCE_ID) FROM BOM WHERE ORG_ID = :orgid ; Explained. SQL>select * from table(dbms_xplan.display) ;

Page 55: Managing SQL Performance

CHAPTER 7 ■ MANAGING SQL PERFORMANCE

5

PLAN_TABLE_OUTPUT ------------------------------------------- Plan hash value: 1639627616 --------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 8 | 1557 (2)| 00:00:19 | | 1 | SORT AGGREGATE | | 1 | 8 | | | |* 2 | TABLE ACCESS FULL| BOM | 607K| 4744K| 1557 (2)| 00:00:19 | --------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - filter("ORG_ID"=TO_NUMBER(:ORGID)) 14 rows selected.

Not only are the operations shown by each display function different, but you can see how the explained version lists estimates for only Rows, Bytes, and Time. The actual data gives you a much more accurate view of what really happened. So why is the explained version so different? The reason for this difference is that EXPLAIN PLAN uses a different code path to determine the execution plan than does the optimizer. That code path treats bind variables like strings and ignores their values. On the other hand, the optimizer considers the values of bind variables and may determine a different plan choice based on those values.

If you use EXPLAIN PLAN, you may believe that the plan will use a set of operations that it does not. And if you want to be successful in managing the performance of your SQL, you need to make decisions based on actual data, not on a “best guess.” The bottom line is that you can’t absolutely rely on EXPLAIN PLAN to give you a clear picture of how your query is performing. So it shouldn’t be your only, or primary, tool for measuring SQL performance. It can’t truly measure performance. And if performance isn’t measurable, it likely isn’t going to be manageable.

If you can’t measure it, you can’t manage it.

—David Garvin

DBMS_XPLAN In Listing 7-1, I used two functions to display execution plan output. Both functions are found in the DBMS_XPLAN supplied package. The DBMS_XPLAN package is used to format SQL execution plan output from EXPLAIN PLAN, SQL stored in the AWR, SQL from a SQL tuning set, as well as for actual cached cursors. This package makes it easy to get a detailed and nicely formatted display of execution plan information.

If you’re an old-timer, like me, you may still have an old script or two that you wrote to manually format EXPLAIN PLAN output. There’s no longer any need for your old scripts, because this package (available since Oracle version 9) does all the work for you. It is flexible enough to handle every display option you need and will automatically display only relevant information. For example, if your plan uses parallel execution, the display function will show you that information. But if you don’t use parallel, the columns containing the data will not be included in the display. It’s a fantastic utility, and you can find a

Page 56: Managing SQL Performance

CHAPTER 7 ■ MANAGING SQL PERFORMANCE

6

lengthy description of the package and the functions it contains in the Oracle PL/SQL Packages and Types Reference for your database version.

For the examples throughout the rest of this chapter, I will use only the DBMS_XPLAN.DISPLAY_CURSOR function. This function is used to display execution plan output for any SQL loaded in the cursor cache. To use this function, you must have SELECT privilege on V$SQL, V$SQL_PLAN, and V$SQL_PLAN_STATISTICS. Note that these privileges are not granted by default to non-DBA users, and it may be necessary to specifically request that they be granted.

The great thing about DISPLAY_CURSOR is that you get a very nicely formatted actual execution plan with, when requested, rowsource execution statistics. As long as you can locate the SQL statement whose execution plan and statistics you wish to view in the shared pool, you can retrieve and display the execution plan data.

The way to call the function is to provide the sql_id and child_number for the SQL statement you want. The sql_id and child_number parameter values are identified in V$SQL (or V$SQLAREA or V$SQLTEXT). If you want to display the execution plan of the last statement executed in your session, you simply leave the parameters null. Listing 7-2 shows a simple example that illustrates a few key pieces of information that are output by DBMS_XPLAN.DISPLAY_CURSOR.

Listing 7-2. Retrieving the sql_id and child_number Values for Use with DBMS_XPLAN.DISPLAY_CURSOR

SQL>variable x number SQL>exec :x := 10 ; PL/SQL procedure successfully completed. SQL>select /*+ GATHER_PLAN_STATISTICS KM2 */ * from dept where deptno = :x; DEPTNO DNAME LOC --------------- -------------- ------------- 10 ACCOUNTING NEW YORK 1 row selected. SQL>SELECT xplan.* 2 FROM 3 ( 4 select max(sql_id) keep 5 (dense_rank last order by last_active_time) sql_id 6 , max(child_number) keep 7 (dense_rank last order by last_active_time) child_number 8 from v$sql 9 where upper(sql_text) like '%&1%' 10 and upper(sql_text) not like '%FROM V$SQL WHERE UPPER(SQL_TEXT) LIKE %' 11 ) sqlinfo, 12 table(DBMS_XPLAN.DISPLAY_CURSOR(sqlinfo.sql_id, sqlinfo.child_number, 'ALLSTATS LAST +PEEKED_BINDS -ROWS')) xplan 13 / Enter value for 1: KM2

Page 57: Managing SQL Performance

CHAPTER 7 ■ MANAGING SQL PERFORMANCE

7

PLAN_TABLE_OUTPUT ------------------------------------- SQL_ID 3h1bp5jsm6d7v, child number 0 ------------------------------------- select /*+ GATHER_PLAN_STATISTICS KM2 */ * from dept where deptno = :x Plan hash value: 3816518310 ----------------------------------------------------------------------------------- | Id | Operation | Name |Starts|A-Rows|A-Time |Buffers | ----------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | 1|00:00.01| 2 | | 1 | TABLE ACCESS BY INDEX RO| DEPT | 1| 1|00:00.01| 2 | |* 2 | INDEX UNIQUE SCAN | DEPT_DEPTNO_PK | 1| 1|00:00.01| 1 | ----------------------------------------------------------------------------------- Peeked Binds (identified by position): -------------------------------------- 1 - (NUMBER): 10 Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("DEPTNO"=:X)

To make it easy to retrieve the sql_id and child_number and execute DISPLAY_CURSOR in the future, I saved Listing 7-2’s query into a script and will execute it at the SQL*Plus command line by supplying my identifying string as a command-line parameter each time I use it (for example, @pln KM2). Note also in Listing 7-2, I added KM2 as my identifying string within the hint. Adding a simple identifier like this will help you quickly locate SQL you are testing.

There are a few things to notice about the SQL I used to retrieve and display the plan:

The GATHER_PLAN_STATISTICS hint: Notice that the SQL statement used in Listing 7-2 includes the GATHER_PLAN_STATISTICS hint. Using this hint is one way to make sure that Oracle correctly gathers rowsource execution statistics during your query execution. You may also set the STATISTICS_LEVEL parameter to ALL for your session, but I find that using the hint lets me gather the rowsource data only when I need it. The rowsource execution statistics are the actual data collected when the query executed. The collected values are displayed in the A-Rows (actual rows), A-Time (actual elapsed time), and Buffers (actual logical block reads) columns in Listing 7-2. These statistics are extremely valuable in determining exactly how the execution plan performed and where you need to focus your attention to optimize the query.

The query used to retrieve the sql_id and child_number: I used the last_active_time column to order the information so that I get back only the most recent (MAX) sql_id and child_number that matches the sql_text pattern I enter. This works almost every time when I’m testing something, particularly if I use a unique identifier in the SQL statement I’m testing. However, if you happen to be looking for a pattern that matches more than one query text and yours wasn’t the last execution, you may not get your plan, but someone else’s. The more specifically you can identify your query, the more likely it is this query will give you the correct plan. If you find you have trouble, you can

Page 58: Managing SQL Performance

CHAPTER 7 ■ MANAGING SQL PERFORMANCE

8

simply query V$SQL for a list of all SQL that matches a certain pattern. Then you can visually identify the correct statement and pass the exact sql_id and child_number into the DISPLAY_CURSOR call.

The format parameters used in the DISPLAY_CURSOR call: The DISPLAY_CURSOR call takes a third parameter named FORMAT. There are numerous options to choose for this parameter value. The default is TYPICAL, but I used a set of options that displayed the data I wanted to show for this example. The use of the ALLSTATS LAST +PEEKED_BINDS –ROWS options causes the actual rowsource execution statistics for the last execution to be displayed along with the values for bind variables used in the SQL, and leaves out the estimated rows column (E-Rows).

Extended SQL Trace Data The Oracle database is instrumented to provide a play-by-play of exactly where your code spends its time. This feature (event 10046) can be enabled on demand by using several methods. The output is a text file that contains two basic types of information: database calls (DB calls) and operating system calls (OS calls). DB call lines begin with the tokens PARSE, EXEC, or FETCH, and each represents a single completed call to the database by the Oracle kernel. OS call lines begin with the token WAIT and represent a single completed call to the operation system by the Oracle kernel. There are also lines that begin with the token STAT that contain the execution plan data. These lines are unformatted, but equivalent to the output you’d get by using DBMS_XPLAN.DISPLAY_CURSOR to retrieve plan data from the cursor cache.

The trace data can be aggregated and summarized to provide a complete response time profile of your code’s execution. That means that you can know exactly where every bit of execution time consumed by your query was used. With trace data, you can get a more complete picture of your query’s response time. The STAT lines give you the rolled-up totals for the query plan and rowsource execution statistics, but you also get the detailed DB calls and any OS calls that contributed to the response time as well.

If you want the definitive tool for helping you diagnose a performance problem, extended SQL trace data gets my vote. In nearly ten years of using trace data and the response time profiles created from them, I have not encountered a performance issue that the trace data didn’t reveal. I know my experience cannot speak to every possible circumstance, and I admit to hearing others speak of several issues where trace data was of little or no assistance. But in my experience, you can’t beat the value of trace data when you want to manage SQL performance.

In Listing 7-3, I use extended SQL trace data to locate a problem that wasn’t visible when examining only the plan data displayed using DBMS_XPLAN.DISPLAY_CURSOR.

First, the query:

select * from bom where item_id=11 and org_id=2;

I executed the query with extended SQL tracing turned on and produced a simple response time profile. The total response time was 1.00 second.

■ Note I used a simple Perl script to create the profile, but Method R Corporation has a software product called the Profiler that can produce response time profiles and is a phenomenal tool for this purpose. Visit http://method-r.com/software/profiler-info for more information.

Page 59: Managing SQL Performance

CHAPTER 7 ■ MANAGING SQL PERFORMANCE

9

Listing 7-3. A Response Time Profile Generated from Extended SQL Trace Data

Response Time Component Duration Pct # Calls Dur/Call ---------------------------------------- --------- ------ --------- ------------ SQL*Net message from client 0.52s 51.9% 12151 0.000043s CPU service 0.30s 29.8% 12155 0.000024s unaccounted-for 0.16s 16.2% 1 0.160985s SQL*Net message to client 0.02s 2.2% 12151 0.000002s ---------------------------------------- --------- ------ --------- ------------ Total response time 1.00s 100.0%

I then used DBMS_XPLAN.DISPLAY_CURSOR to review just the execution plan data, as shown in Listing 7-4.

Listing 7-4. Using DISPLAY_CURSOR to Display Execution Plan Data

------------------------------------------------------------------------------------ | Id | Operation | Name | Starts | A-Rows | A-Time | Buffers | ------------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 1 | 24294 |00:00.10 | 24472 | | 1 | TABLE ACCESS BY INDEX ROWID| BOM | 1 | 24294 |00:00.10 | 24472 | |* 2 | INDEX RANGE SCAN | BOM_N1 | 1 | 24294 |00:00.02 | 12208 | ------------------------------------------------------------------------------------ Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("ITEM_ID"=11 AND "ORG_ID"=2)

Just so you can also see how this information looks in the trace file, Listing 7-5 shows the STAT lines.

Listing 7-5. STAT Lines from the Extended SQL Trace Data

STAT #6 id=1 cnt=24294 pid=0 pos=1 obj=71478 op='TABLE ACCESS BY INDEX ROWID BOM (cr=24472 pr=0 pw=0 time=100007 us cost=285 size=1481934 card=24294)' STAT #6 id=2 cnt=24294 pid=1 pos=1 obj=71480 op='INDEX RANGE SCAN BOM_N1 (cr=12208 pr=0 pw=0 time=39970 us cost=60 size=0 card=24294)'

Now what? Is 1 second acceptable? Well, I expect this query to execute hundreds or thousands of times every hour, and it is not reasonable for a single execution to take a second. But if you look at the plan statistics, you’ll notice that the A-Time column is showing only 1 centisecond of elapsed time consumed to access 1,252 buffers and retrieve 24,294 rows. A centisecond seems reasonable. It appears that the correct index was used because the index returned only the rowids that the parent table access step acquired and kept. But why did my response time profile’s actual elapsed time of 1 second not match the time shown in the execution plan? It looks like one or the other is wrong, doesn’t it?

The truth is that both of the timings are correct. The problem is that we assumed both timings measure the same thing, and they do not. To be more precise, the centisecond timing shown for the plan execution is included in the 1 second total response time. The time shown in the A-Time column represents the time it took to complete the plan operations: an index range scan and a TABLE ACCESS BY INDEX ROWID. That means there are other events that are not included in the plan execution display provided through DBMS_XPLAN.DISPLAY_CURSOR.

Page 60: Managing SQL Performance

CHAPTER 7 ■ MANAGING SQL PERFORMANCE

10

However, if you look at the response time profile, you can clearly see that the majority of the time was spent doing 12,151 SQL*Net message from client calls. That call indicates that the database currently isn’t doing anything, but rather is waiting to be told what to do. So can you guess why all those calls are being made? It’s due to the number of rows each FETCH call is limited to return in a single retrieval. In SQL*Plus, this value is set with a parameter called ARRAYSIZE. In this example, ARRAYSIZE was set to 2. That means that only 2 rows at a time are sent to the client. Because we had more than 24,000 rows to be returned, more than 12,000 network roundtrips were required to get the entire result set back to the client application.

But would we really want so many round trips to happen? What if ARRAYSIZE were set to a higher value? If ARRAYSIZE was set to 100, for example, then 100 rows per FETCH call would be retrieved and sent to the client. Therefore, given that we have approximately 24,000 total rows to be returned, it would be necessary to make about 240 calls to do so. I can now forecast how much of a response time reduction I can get if I make this change. Roughly, if only 240 SQL*Net message from client calls are made instead of more than 12,000, I can estimate the time to make those calls will reduce from 0.52 seconds (12,151 calls taking 0.000043 seconds each) to approximately 0.01 seconds (240 calls). That’s simple enough, and it looks like a significant response time difference.

Listing 7-6 shows the response time profile for the same query executed after I changed the ARRAYSIZE parameter to 100.

Listing 7-6. The Response Time Profile After the ARRAYSIZE Change

Response Time Component Duration Pct # Calls Dur/Call ---------------------------------------- --------- ------ --------- ------------ SQL*Net message from client 0.14s 70.1% 247 0.000549s CPU service 0.05s 24.2% 251 0.000186s unaccounted-for 0.01s 5.5% 1 0.010728s SQL*Net message to client 0.00s 0.2% 247 0.000002s ---------------------------------------- --------- ------ --------- ------------ Total response time 0.19s 100.0%

That’s better! My estimate was a little bit off because the duration per call was higher for this test than for the original, but I was pretty much on target with what I expected to happen. Being able to see the entire performance picture, not just one piece of it, and then accurately forecast what will happen if you make a change is the real magic of using extended SQL trace.

I could continue to experiment with ARRAYSIZE to locate the best setting, but for my purpose here, this is good enough. The point is that with just the plan execution information I reviewed by using DBMS_XPLAN.DISPLAY_CURSOR, I couldn’t fully diagnose the problem. With just that portion of the total picture, I was missing a critical piece of information that I needed. After I had that information, it was a simple matter to see where my time was being used and find a way to reduce it.

So why not just use extended SQL trace all the time? Well, in some cases, it’s more difficult to use it and get access to the generated trace files than it is to just grab the plan execution information quickly from the cursor cache. When tracing, you want to make sure to properly scope the trace so you capture only the data for the task you care about. Capturing extra activity that you don’t care about can distort the response time profile so your task is masked within a bunch of other unimportant stuff. Also, in order to retrieve the trace file, you must have permission to access the dump file directory on your database server and then have a tool (either one you create or one you purchase) that can create a response time profile for you. Sometimes working with and around those limitations makes the use of extended SQL trace require more time and effort than some people are willing to expend.

Just remember that you have multiple tools in your performance toolbox. Use the tool that’s appropriate for the task at hand. The key is to know when to use which tool. One of my favorite sayings is

Page 61: Managing SQL Performance

CHAPTER 7 ■ MANAGING SQL PERFORMANCE

11

“Why guess when you can know?” And because I want to know exactly where all my response time is going, I find using extended SQL trace invaluable. Just remember that in order to manage SQL performance well, you want to know, not just guess.

Interpreting Performance Data After you execute your query and collect the performance data, what’s next? If you can get the information but don’t know how to respond to what you see, you’re really no further along toward solving the problem. It’s often easiest to learn by example, so I’m going to review four cases that demonstrate common problems I see over and over. I’ll walk you through each example and point out how to interpret what the performance data is telling you.

Case 1: The Lack of a Good Index One performance problem I run into quite frequently is caused by the absence of an index that properly covers the query predicate. There may be many indexes on the table, but none of them provide the optimal coverage for the query in question. Many times the available indexes are for only a single column, but the query predicate includes several columns. So even if the optimizer chooses to use a single-column index, a table block access will be required to retrieve the row and then apply the other filter conditions before the determination to keep the row for the result set can finally be made.

The plan execution data provides you with a clear indication of when the index being used could benefit from having additional columns. In Listing 7-7, I display only two lines, along with just their A-Rows values and the related predicate information lines, from a very long plan to point out how the index in use isn’t very effective.

Listing 7-7. Excerpt of Execution Plan When a “Good” Index Does Not Exist

----------------------------------------------------------------------- | Id | Operation | Name | A-Rows | ----------------------------------------------------------------------- |* 32 | TABLE ACCESS BY INDEX ROWID | GL_ACCOUNT_INSTANCE | 606K| |* 33 | INDEX UNIQUE SCAN | GL_ACCOUNT_INSTANCE_PK | 3183K| Predicate Information (identified by operation id): --------------------------------------------------- 32 - filter(("GLI"."GL_ACCT_STAT_CD"='ACTIVE' AND "TD"."GL_ACCT_NAME_CD"="GLI"."GL_ACCT_NAME_CD")) 33 - access("TD"."GL_SYS_ID"="GLI"."GL_SYS_ID")

Notice how the index range scan shows a large number of rowids being passed to the parent table access step (more than 3.1 million), but less than 20 percent of those rows (606,000) are retained for the final result set. Now look at the Predicate Information for the parent table access step (Id = 32). Two columns have to be obtained from the row in the data block and then filtered before the row is finally accepted or rejected. If those two columns were added to the index, or if a new index with all three columns was added, 80 percent of the work currently being performed to access all the data blocks that ultimately get thrown away would be avoided. In Listing 7-8, I show the difference in the plan after the new index is created.

Page 62: Managing SQL Performance

CHAPTER 7 ■ MANAGING SQL PERFORMANCE

12

Listing 7-8. Excerpt of Execution Plan When a “Good” Index Does Exist

------------------------------------------------------------------------ | Id | Operation | Name | A-Rows | ------------------------------------------------------------------------ | 32 | TABLE ACCESS BY INDEX ROWID | GL_ACCOUNT_INSTANCE | 606K| |* 33 | INDEX RANGE SCAN | GL_ACCOUNT_INSTANCE_IX5 | 606K| Predicate Information (identified by operation id): --------------------------------------------------- 33 - access("TD"."GL_SYS_ID"="GLI"."GL_SYS_ID" AND "GLI"."GL_ACCT_STAT_CD"='ACTIVE' AND "TD"."GL_ACCT_NAME_CD"="GLI"."GL_ACCT_NAME_CD")

It was easy to see how having the filter conditions applied at the parent table access step caused more work to occur. The more often an index can provide complete coverage for the conditions in your SQL, the less work you incur to get the final result set. It may not always be possible to create an index to cover your predicates completely. However, if performance for a particular query is critical, you’ll want to make sure that indexes provide the highest coverage possible.

Case 2: The Presence of Unidentified Data Skew Query response times can vary significantly depending on the plan operations chosen by the optimizer. The optimizer depends on statistics to be able to determine the best plan choices. If the available statistics do not accurately represent your data, the optimizer’s estimates for plan choice may result in operations that aren’t as performance optimal as needed. A quick review of plan execution data lets you see how the optimizer’s estimates stack up to the actual usage. Listing 7-9 shows the execution plan data for a query that requests only data where the object_type column contains the value ‘PROCEDURE’.

Listing 7-9. Execution Plan for Query Using object_type = ‘PROCEDURE’

------------------------------------------------------------------------------------ | Id | Operation | Name |E-Rows |A-Rows |A-Time |Buffers | ------------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | | 4416 |00:00.01 | 1586 | | 1 | TABLE ACCESS BY INDEX R| B | 59403 | 4416 |00:00.01 | 1586 | |* 2 | INDEX RANGE SCAN | B_OBJTYPE_IDX | 59403 | 4416 |00:00.01 | 105 | ------------------------------------------------------------------------------------ Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("OBJECT_TYPE"='PROCEDURE')

Compare the E-Rows and A-Rows columns in Listing 7-9. It’s pretty obvious that there is a significant discrepancy between the estimate and the actual values (over 13 times difference). But, the response time, of approximately 1 centisecond, wasn’t a problem. If I used only EXPLAIN PLAN output, I wouldn’t have noticed this difference at all and may have stopped my analysis right there.

But look at Listing 7-10 to see what happens when you use a different object_type value for the same query.

Page 63: Managing SQL Performance

CHAPTER 7 ■ MANAGING SQL PERFORMANCE

13

Listing 7-10. Execution Plan for Query Using object_type = ‘SYNONYM’

----------------------------------------------------------------------------------- | Id | Operation |Name |E-Rows |A-Rows |A-Time |Buffers | ----------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | | 858K|00:18.88 | 62952 | | 1 | TABLE ACCESS BY INDEX R|B | 59403 | 858K|00:18.88 | 62952 | |* 2 | INDEX RANGE SCAN |B_OBJTYPE_IDX | 59403 | 858K|00:00.86 | 19441 | ----------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("OBJECT_TYPE"='SYNONYM')

Ouch! This time the response time is almost 19 seconds. The plan is the same, an index range scan, but when the query returns more data than estimated, the response time is now unacceptable. You can also see that not only has the response time increased, but the buffers have increased significantly as well. This makes sense because the query retrieved more than 850,000 rows.

There’s quite a difference between 1,586 buffer gets and 62,952 buffer gets for the two queries. However, there is an even more significant difference that you don’t see. There is a statistic called buffer is pinned count that is viewable only if you snapshot that specific statistic name from V$SESSTAT before and after you execute your query and then diff the results. That means you won’t see it when using DISPLAY_CURSOR or even in extended SQL trace data.

This statistic contains a count of buffer accesses for blocks previously pinned into the cache. The fact that those blocks are already pinned means there is less overhead to access them and requires less work on Oracle’s part (particularly less latching). The main point I want to make is that the Buffers count is only part of the whole picture for an index access operation. Don’t forget that more work is being done!

In this case, the buffer is pinned count value for this query is 1,655,993. That means over 1.7 million buffers were accessed to satisfy the query result. No wonder it took so long! As I said, the buffer is pinned count statistic isn’t directly included in either extended SQL trace output or from the cursor cache. You have to compute it yourself for a single query execution. Although knowing about this statistic isn’t critical to how you’d respond to what you see in this execution plan, I think it is important to know there is more data available that completes the picture of the work this query is doing.

If you’d like to be able to view statistics such as buffer is pinned count, capture latch statistics, and more, there are two sets of utility scripts that I use frequently to to do this: Tom Kyte’s runstats package (http://asktom.oracle.com/pls/asktom/f?p=100:8:0::NO) and the Hotsos SQL Test Harness (www.hotsos.com/educ_downloads.html). Both sets of scripts are easy to install and use, and help you collect even more performance data you can use.

The bottom line in this example is that the optimizer didn’t have the correct information to know that there was a skewed distribution of values in the object_type column. By default, statistics are gathered assuming that all data values are uniformly distributed. If that is not the case, statistics must be gathered specifically to capture skew. Skew is captured by the creation of a histogram and is done using the METHOD_OPT parameter when gathering object statistics as follows: SQL> exec dbms_stats.gather_table_stats (user,'B',estimate_percent=>100, method_opt=>'for columns object_type');

Page 64: Managing SQL Performance

CHAPTER 7 ■ MANAGING SQL PERFORMANCE

14

Listings 7-11 and 7-12 show how after the histogram is in place, the estimates for the two queries are accurate. Also, the plan choices are appropriate for each predicate, not to mention that the response time for the second query improves significantly.

Listing 7-11. Execution Plan for Query Using object_type = ‘PROCEDURE’ with Histogram in Place

------------------------------------------------------------------------------------ | Id | Operation | Name |E-Rows |A-Rows |A-Time |Buffers | ------------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | | 4416 |00:00.01 | 1586 | | 1 | TABLE ACCESS BY INDEX R| B | 4416 | 4416 |00:00.01 | 1586 | |* 2 | INDEX RANGE SCAN | B_OBJTYPE_IDX | 4416 | 4416 |00:00.01 | 105 | ------------------------------------------------------------------------------------ Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("OBJECT_TYPE"='PROCEDURE')

Listing 7-12. Execution Plan for Query Using object_type = ‘SYNONYM’ with Histogram in Place

------------------------------------------------------------------------------------ | Id | Operation | Name | E-Rows | A-Rows | A-Time | Buffers | Reads | ------------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | | 858K|00:00:03.49 | 49242 | 22224 | |* 1 | TABLE ACCESS FULL| B | 858K| 858K|00:00:03.49 | 49242 | 22224 | ------------------------------------------------------------------------------------ Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("OBJECT_TYPE"='SYNONYM')

Although this is a very simple example of how the presence of data skew can cause the optimizer estimates to be orders of magnitude off, the ability to compare estimated and actual values using DBMS_XPLAN.DISPLAY_CURSOR makes the issue easy to spot and correct quickly. Finding the presence of skew isn’t the only time when having the estimated vs. actual comparison will pay off. But the point is that regardless of the cause, the ability to see both values at a glance makes it obvious if there is an issue at all.

Case 3: SQL That Should Be Rewritten In some cases, you’ll find that the performance problem you’re reviewing can be fixed without touching the code, as you’ve seen in the previous examples. In my experience, however, often the best fix is to rewrite the SQL. Of course, if you can’t touch the code, you’ll have to find another way around, such as using stored outlines or SQL profiles. But even if you can’t change the code as the final solution, you may need to modify the code in order to determine the best execution plan possible so you can save and enable it easily.

Page 65: Managing SQL Performance

CHAPTER 7 ■ MANAGING SQL PERFORMANCE

15

Listing 7-13 shows a seemingly simple query, but after reviewing the performance data, you’ll see that rewriting it would significantly improve its performance.

Listing 7-13. Query and Execution Plan with an Easy-to-Miss Scalability Issue

SELECT ATTR_FCTR, ATTR_FCTR_YR, ATTR_FCTR_MO, PROJECT_CODE FROM PRJ_ATT_FACTORS A WHERE SIM_YR || SIM_MO = (SELECT MAX(B.SIM_YR || B.SIM_MO) FROM PRJ_ATT_FACTORS B WHERE A.PROJECT_CODE = B.PROJECT_CODE); ----------------------------------------------------------------------------------- | Id | Operation | Name |Starts |A-Rows |A-Time | Buffers | ----------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 16 |00:00.01 | 71 | |* 1 | FILTER | | 1 | 16 |00:00.01 | 71 | | 2 | TABLE ACCESS FULL | PRJ_ATT_FACTORS | 1 | 25 |00:00.01 | 8 | | 3 | SORT AGGREGATE | | 9 | 9 |00:00.01 | 63 | |* 4 | TABLE ACCESS FULL| PRJ_ATT_FACTORS | 9 | 25 |00:00.01 | 63 | ----------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("SIM_YR"||"SIM_MO"=) 4 - filter("B"."PROJECT_CODE"=:B1)

This particular query has a subquery in the WHERE clause that returns the MAX year/month for a given project code. So for each row in PRJ_ATT_FACTORS, the subquery will execute as the row is filtered through the WHERE clause. That’s not precisely true, because subquery caching comes into play. Subquery caching has the effect of requiring that the subquery be executed just once per distinct project code instead of once per row.

■ Note A wealth of information on subquery caching is available by doing a simple Google search on Oracle subquery caching.

Consider the ground that must be covered to obtain the final result set. My test table has 25 rows (Id = 2, A-Rows = 25). For those 25 rows, Oracle executes the MAX subquery nine times (Id = 3, Starts = 9). Because of subquery caching and because there are only nine distinct project codes in my table, it executes only nine times. After a project code is used in the subquery once, its result is retained in memory and reused, and doesn’t require Oracle to issue the subquery again. But the point is that the subquery gets executed over and over again as new project codes occur.

At this point, you may be wondering why I don’t just create an index on the project_code column so that instead of the subquery plan doing a full table scan, it would use the index. I could do that. But even if I create the index and it improves things, by reducing buffer gets and therefore likely time as well, the

Page 66: Managing SQL Performance

CHAPTER 7 ■ MANAGING SQL PERFORMANCE

16

query will still be making repeated executions of the subquery. I want to find a way to avoid the repeated executions.

Also notice that the subquery is against the same table I’m accessing in the outer query. So the primary question I’d want to consider is “Can I write this query differently to avoid multiple accesses against the same table over and over?” Right now, the way the query is written, there’s not much that can be done to make it scale very well. It may perform okay now, but what about when the volume of data grows? Listing 7-14 shows what happens if I double the table size to 50 rows.

Listing 7-14. Execution Plan When the Data Size (Number of Rows) in the Test Database Doubles

----------------------------------------------------------------------------------- | Id | Operation | Name |Starts |A-Rows |A-Time | Buffers | ----------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 32 |00:00.01 | 127 | |* 1 | FILTER | | 1 | 32 |00:00.01 | 127 | | 2 | TABLE ACCESS FULL | PRJ_ATT_FACTORS | 1 | 50 |00:00.01 | 8 | | 3 | SORT AGGREGATE | | 17 | 17 |00:00.01 | 119 | |* 4 | TABLE ACCESS FULL| PRJ_ATT_FACTORS | 17 | 50 |00:00.01 | 119 | ----------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("SIM_YR"||"SIM_MO"=) 4 - filter("B"."PROJECT_CODE"=:B1)

The number of executions of the subquery increases from 9 to 17 (check the Starts column), and the buffers accessed increases from 71 to 127. Just to give you an idea of how bad this gets as volume increases, Listing 7-15 shows this same query’s plan execution data from production; the table has more than 270,000 rows.

Listing 7-15. Execution Plan from Production

------------------------------------------------------------------------------------ | Id | Operation |Name |Starts |A-Rows | A-Time |Buffers | ------------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 1 | 18480 |04:13:58.24 | 52756K| |* 1 | FILTER | | 1 | 18480 |04:13:58.24 | 86529 | | 2 | TABLE ACCESS FULL |PRJ_ATT_FACTORS| 1 |271830 |00:00:01.53 | 3053 | | 3 | SORT AGGREGATE | |142578 | 28843 |04:13:11.10 | 52667K| |* 4 | TABLE ACCESS FULL |PRJ_ATT_FACTORS|142578 | 14816K|04:11:33.55 | 52667K| ------------------------------------------------------------------------------------ Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("SIM_YR"||"SIM_MO"=) 4 - filter("B"."PROJECT_CODE"=:B1)

In production, the response time for this query was more than 4 hours! So, how could I rewrite this query to eliminate the need to query the same table over and over, and do it only once? How about using

Page 67: Managing SQL Performance

CHAPTER 7 ■ MANAGING SQL PERFORMANCE

17

an analytic function? Listing 7-16 shows the rewritten query and the new plan execution data using my test environment with a 50-row table.

Listing 7-16. Rewritten Query and Execution Plan

SELECT ATTR_FCTR, ATTR_FCTR_YR, ATTR_FCTR_MO, PROJECT_CODE, THE_YRMO FROM ( SELECT MAX(SIM_YR || SIM_MO) OVER (PARTITION BY PROJECT_CODE) AS THE_MAX, ATTR_FCTR, ATTR_FCTR_YR, ATTR_FCTR_MO, PROJECT_CODE, SIM_YR || SIM_MO AS THE_YRMO FROM PRJ_ATT_FACTORS ) a WHERE a.THE_YRMO = THE_MAX ; ------------------------------------------------------------------------------------ | Id | Operation |Name |Starts |A-Rows | A-Time |Buffers | ------------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 1 | 32 |00:00:00.01 | 7 | |* 1 | VIEW | | 1 | 32 |00:00:00.01 | 7 | | 2 | WINDOW SORT | | 1 | 50 |00:00:00.01 | 7 | | 3 | TABLE ACCESS FULL|PRJ_ATT_FACTORS | 1 | 50 |00:00:00.01 | 7 | ------------------------------------------------------------------------------------ Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("A"."THE_YRMO"="THE_MAX")

This rewritten version of the query requires only a single access on the table. Much better! Listing 7-17 shows how the rewritten query performed in production.

Listing 7-17. Rewritten Query’s Production Execution Plan

------------------------------------------------------------------------------------ | Id | Operation |Name |Starts |A-Rows | A-Time |Buffers | ------------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 1 | 18480 |00:00:11.47 | 1833 | |* 1 | VIEW | | 1 | 18480 |00:00:11.47 | 1833 | | 2 | WINDOW SORT | | 1 |271830 |00:00:09.73 | 1833 | | 3 | TABLE ACCESS FULL|PRJ_ATT_FACTORS | 1 |271830 |00:00:01.59 | 1826 | ------------------------------------------------------------------------------------ Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("A"."THE_YRMO"="THE_MAX")

Page 68: Managing SQL Performance

CHAPTER 7 ■ MANAGING SQL PERFORMANCE

18

I’d say this is a very successful optimization effort! The response time decreased from more than 4 hours to a little more than 11 seconds.

The most difficult part of this problem was determining how to rewrite the query. That part gets easier with practice, but it is important to stay on top of new coding features and syntax options that are available in the version of Oracle you are using. SQL like this has often been around for a long time, and it’s easy to see the code only as it is and not think of new possibilities for how it could be written more efficiently.

Almost every SQL statement can be written in multiple ways. Just make sure that when you are considering ways to write SQL, your options aren’t limited by a lack of knowledge. Sometimes overcoming not knowing what you can do is the biggest performance hurdle you’ll have to cross.

Case 4: SQL That Unnecessarily Invokes PL/SQL The issue of concern in this case is called context switching. SQL is a nonprocedural language and has a separate execution code path, often referred to as the engine, within the Oracle kernel. PL/SQL is a procedural language and has its own execution engine. In order for a SQL statement to call a PL/SQL code block, or a PL/SQL code block to execute a SQL statement, Oracle must switch between execution engines. This switch means extra overhead is incurred that would not otherwise be required if the switch did not happen. This “hit,” even if it is very small, say 1/1000th of a second, will add up if you do it enough. For example, take a SQL statement that returns 10,000 rows. If each of those result rows has a PL/SQL function call, that would mean 10 seconds of your response time would be consumed just executing those calls (10,000 rows × 0.001 seconds). Can you really afford that extra response time consumption?

Before you jump to the conclusion that I’m making a blanket statement that PL/SQL is bad for performance and should be avoided, let me stop you. I am absolutely not saying that. What I want you to know is that if you have a need to execute a PL/SQL code block that provides you with a result you cannot achieve directly through SQL, you will pay a performance price for using it. So one thing to look for when your SQL performance is suffering is the presence of PL/SQL calls when you could get the same result another way.

The tricky part about this issue is that if you look only at the SQL execution plan data, you won’t see the problem, except perhaps in terms of response time. But in your test environment where you may have only small tables to query against, even response time may not be poor enough to alert you. Listing 7-18 shows a PL/SQL function and two queries we’ll use to walk through an example.

Listing 7-18. Example Queries and PL/SQL Function

create or replace function get_ord_tot(p_ordno IN number) return number as v_tot number(13,2); begin select sum(total_order_item_price) into v_tot from items where order_no = p_ordno; return v_tot; exception when others then return 0; end; /

Page 69: Managing SQL Performance

CHAPTER 7 ■ MANAGING SQL PERFORMANCE

19

select cust_no, order_no, get_ord_tot(order_no) from orders group by cust_no, order_no; select o.cust_no, o.order_no, sum(i.total_order_item_price) from orders o, items i where o.order_no = i.order_no group by o.cust_no, o.order_no;

In the first query, the function GET_ORD_TOT is called to retrieve the total order amount, whereas in the second query, the same result is achieved using a table join. Listings 7-19 and 7-20 list the execution plan data for each query.

Listing 7-19. Execution Plan Data for a Query Using a PL/SQL Function

------------------------------------------------------------------------------ | Id | Operation | Name | Starts | A-Rows | A-Time | Buffers | ------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 1 | 12890 |00:00:00.02 | 156 | | 1 | HASH GROUP BY | | 1 | 12890 |00:00:00.02 | 156 | | 2 | TABLE ACCESS FULL| ORDERS | 1 | 12890 |00:00:00.01 | 156 | ------------------------------------------------------------------------------

Listing 7-20. Execution Plan Data for a Query Using a Table Join

----------------------------------------------------------------------------------- | Id | Operation | Name | Starts | A-Rows | A-Time | Buffers | ----------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 12890 |00:00:00.10 | 436 | | 1 | HASH GROUP BY | | 1 | 12890 |00:00:00.10 | 436 | |* 2 | HASH JOIN | | 1 | 12890 |00:00:00.08 | 436 | | 3 | TABLE ACCESS FULL | ORDERS | 1 | 12890 |00:00:00.01 | 156 | | 4 | VIEW | VW_GBC_5 | 1 | 12890 |00:00:00.08 | 280 | | 5 | HASH GROUP BY | | 1 | 12890 |00:00:00.08 | 280 | | 6 | TABLE ACCESS FULL| ITEMS | 1 | 70975 |00:00:00.01 | 280 | ----------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("O"."ORDER_NO"="ITEM_1")

Compare both listings. At a glance, Listing 7-19 appears to be better in terms of overall time (A-Time) and logical I/O (Buffers). But with just a bit more scrutiny, you should notice that the first query plan doesn’t contain any operations involving the ITEMS table. Huh? Did using the function call just give that data access to you for free? Obviously, it did not. What the function call did was to effectively hide the access on the ITEMS table from your view. Execution plans show only a single query’s data. Queries that call PL/SQL that contains additional SQL will not be included in the execution plan data for the query you executed. The execution plan data for the second query using the table join contains everything you

Page 70: Managing SQL Performance

CHAPTER 7 ■ MANAGING SQL PERFORMANCE

20

need, but you need more data for the first query. So if you compare just these two listings, you’re really not comparing the complete picture of both executions.

To get a complete picture of each query execution for comparison, this is a case where you can benefit from collecting extended SQL trace data. Recall that with trace data you get all the execution data, including time spent waiting for events that don’t show up in the plan data alone. So for each test, I collected trace data and created response time profiles, as shown in Listings 7-21 and 7-22.

Listing 7-21. Response Time Profile for a Query Using a PL/SQL Function

Response Time Component Duration Pct # Calls Dur/Call ---------------------------------------- --------- ------ --------- ------------ CPU service 3.04s 76.3% 281 0.010826s SQL*Net message from client 0.82s 20.7% 270 0.003050s unaccounted-for 0.10s 2.6% 1 0.104507s pipe get 0.02s 0.4% 1 0.015347s SQL*Net message to client 0.00s 0.0% 270 0.000002s ---------------------------------------- --------- ------ --------- ------------ Total response time 3.99s 100.0%

Listing 7-22. Response Time Profile for a Query Using a Table Join

Response Time Component Duration Pct # Calls Dur/Call ---------------------------------------- --------- ------ --------- ------------ SQL*Net message from client 0.84s 79.5% 270 0.003109s CPU service 0.11s 10.3% 281 0.000389s unaccounted-for 0.07s 6.9% 1 0.072339s pipe get 0.03s 3.2% 1 0.034077s SQL*Net message to client 0.00s 0.0% 270 0.000002s ---------------------------------------- --------- ------ --------- ------------ Total response time 1.06s 100.0%

From the two profiles in Listings 7-21 and 7-22, note in particular the total time spent executing CPU service calls. When using the PL/SQL function, 281 calls take 3.04 seconds to complete. But when using a table join, the same number of calls takes only 0.11 seconds. Also notice the difference in duration per call: 0.010826 seconds per call using the function as compared to 0.003109 seconds per call for the join. With this data, it appears pretty obvious that using the PL/SQL function has significantly greater impact than execution plan data alone seemed to indicate.

To take it one step further, Listing 7-23 shows the output from the Oracle utility tkprof I used to summarize the trace file containing the PL/SQL function. With this summarization, I can show you where the “missing” function calls and their related data are.

Listing 7-23. tkprof Output Excerpt

SQL ID: buy1cavrwf8ju Plan Hash: 1902515569 SELECT SUM(TOTAL_ORDER_ITEM_PRICE) FROM ITEMS WHERE ORDER_NO = :B1

Page 71: Managing SQL Performance

CHAPTER 7 ■ MANAGING SQL PERFORMANCE

21

call count cpu elapsed disk query current rows ------- ------ -------- ---------- ---------- ---------- ---------- ---------- Parse 1 0.00 0.00 0 0 0 0 Execute 12890 1.66 1.53 0 0 0 0 Fetch 12890 0.21 0.16 0 39124 0 12890 ------- ------ -------- ---------- ---------- ---------- ---------- ---------- total 25780 1.87 1.69 0 39124 0 12890

The main query execution plan in Listing 7-19 showed only the access to the ORDERS table (12,890 rows). Using the trace data, we can look further into exactly what happened when that query executed. We can see that for each of those 12,890 rows, the query against the ITEMS table in the function was executed using the ORDER_NO as the input parameter (:B1).

Note that I’m not showing you exactly the amount of time accounted for by context switching. It is included in the CPU service time required to execute the function call over and over. But when you add up the overall effect of executing all those function calls plus the overhead of switching between the SQL and PL/SQL engine to do them, I think you get the picture!

I’ll end by saying that the use of functions such as the one demonstrated here is most often done in the name of reusability or ease of maintenance. The argument is that if you know you’ll want to use that same query against the ITEMS table in many places and if you put it in a function, you’ll have only one place to make a change should one be required. I disagree with the premise of this argument that the ability to reuse or maintain application code is the first priority. PL/SQL is a procedural language extension to SQL; SQL is the core. If you’ve accepted the performance mindset as your own, you wouldn’t choose to do anything that would degrade performance at the expense of perceived savings in maintaining that SQL. The more you code big blocks of PL/SQL that could be written in one SQL statement, you’ve added more code to maintain, added more complexity and opportunity for bugs, and certainly added overhead that will hit your application every day and not just during a maintenance cycle. If performance is your goal, doing everything you can in SQL directly should be your first choice.

Summary To manage SQL performance well requires a commitment to the process. Beginning with your mindset, you accept responsibility for the performance of every SQL statement you write or maintain. A big part of fulfilling your responsibility is to keep your education up-to-date. You must know how to accurately collect and analyze performance data to move quickly and efficiently from problem diagnosis to a solution.

It’s not really that hard to get the data you need. We’ve reviewed a couple of ways in this chapter. Regardless of the method you use to collect and review performance data, the key is to stop guessing about performance problems. Remember: Why guess when you can know?

You won’t be able to count on every SQL performance problem you encounter being something you’ve seen before. There are certainly common problems that can, and do, occur. But as you grow more adept at managing SQL performance, you’ll find that you no longer make the mistakes that lead to those kinds of problems in the first place. The key is to be able to pinpoint the cause of the problem quickly. If you don’t know exactly how to proceed after you’ve identified the problem, a little bit of research should lead you to the information you need to finalize a solution.

Optimal performance doesn’t just happen. Nor is it that hard to accomplish, even though many people believe it is. To achieve consistently high-performing SQL requires time, knowledge, and diligence. To do it well, you need to integrate concern for performance into your daily routine. After

Page 72: Managing SQL Performance

CHAPTER 7 ■ MANAGING SQL PERFORMANCE

22

you’ve made SQL performance management part of your routine, you’ll likely find that the performance issues you face will be fewer and easier to deal with when they do occur.

Further Reading Wolfgang Breitling, “Tuning by Cardinality Feedback,” www.centrexcc.com/Tuning%20by%20Cardinality%20Feedback.pdf

Wolfgang Breitling, “SQL Tuning with Statistics,” www.centrexcc.com/SQL%20Tuning%20with%20Statistics.pdf

Cary Millsap, “For Developers: Making Friends with the Oracle Database,” http://method-r.com/downloads/doc_download/10-for-developers-making-friends-with-the-oracle-database-cary-millsap

Karen Morton, “Managing Statistics for Optimal Query Performance,” http://method-r.com/downloads/doc_download/11-managing-statistics-for-optimal-query-performance-karen-morton

Karen Morton, “Performance Instrumentation for PL/SQL,” http://method-r.com/downloads/doc_download/8-performance-instrumentation-for-plsql-when-why-how-karen-morton

Page 73: Managing SQL Performance

! ! ! ! !! !

! ! ! ! !

!

!

I55%')6@!Y!

!

C>-5(%$!"a!B$/4!!

#$/!F$-?G%!3OP!

I5$%&&K!08"8!

Page 74: Managing SQL Performance

! ! !

!

! ! ! ! ! ! ! ! ! ! !

Page 75: Managing SQL Performance

C H A P T E R 1 5

■ ■ ■

465

Testing and Quality Assurance

Robyn Sands

As you've worked through the chapters of this book, you may have written some code to test the examples. And since you chose this particular book instead of a “Welcome to SQL” style book, it's likely that you had written quite a few SQL statements before you ever picked this book up. As you've read this book, did some of the chapters remind you of your prior work? If so, how did you feel about the code you've written in the past?

If you're like most developers, there were times when you thought, “Hey, considering how little I knew about this functionality back then, I did pretty well.” And there may have been a few times when you cringed a bit, realizing that something you were very proud of at the time wasn't such a great approach after all. Don't worry; we all have applications that we would write completely differently if we only knew then what we know now. Besides, it's always easier to write better code with hindsight vision or as an armchair code jockey.

If the code you write today is better than the code you wrote yesterday, you’re continuing to grow and learn, and that is commendable. Realizing our older work could have been done better is an inevitable part of the learning process. As long as we learn from our mistakes and do it a little better with the next application or the next bit of code, we're moving in the right direction.

It’s also true that we need to be able to measure the quality of our current code now, not five years from now when we've grown even wiser. We want to find the problems in our code before those problems affect our users. Most of us want to find all the errors or performance issues before anyone else even sees our work. However, while that kind of attitude may indicate an admirable work ethic, it's not an advisable or even achievable goal. What we can achieve is a clear definition what a specific piece of code needs to accomplish and how we will prove that the code meets the defined requirements. Code should have measurable indicators of success that can prove or disprove the fact that we have met our goal.

So what are those measurable factors? While the target measurement will vary depending on the application, there are several basic requirements for all application code. First and foremost, the code needs to return accurate results and we need to know that results will continue to be accurate throughout the system’s life cycle. If end users cannot count on the data returned by a database application, that’s a pretty serious failure.

Performance is another measurable attribute of our code. The target run times will be highly dependent on the application in question: a database used by the home owner's association to track who has paid their annual fees is not required to perform at the same level as a database containing the current stock quotes, but the methods used to compare execution plans and measure run time can be the same. Code quality requires that we understand the application requirements, the function being performed, and the strengths and weaknesses of the specific system. Testing should focus on verifying functionality, pushing the weakest links to their breaking point, and recording all measurements along the way.

Page 76: Managing SQL Performance

CHAPTER 15 ■ TESTING AND QUALITY ASSURANCE

466

Test Cases For the examples in this chapter, you will be working with the same Order Entry sample schema that you used for the transaction processing examples in Chapter 14. You will make more changes to your schema, adding new data and altering views and reports. You will begin by defining the changes to be made and the tests you will use to verify the success of those changes.

So here is the backstory: one of your suppliers, identified only as “Supplier 103089” in the database, is changing their product numbers for the software you purchase from them to resell to your customers. The new identifiers are appended with a '-' and a two character value to identify the software package language. For example, the supplier's product identifier for all English software packages will end in “-EN”. The supplier will require their product identifier to be referenced for ordering, software updates, and warranty support. The new product identifiers have an effective date of October 10, 2010.

This change presents the following challenges for your company:

• The Order Entry schema includes the supplier’s identifier in the PRODUCT_INFORMATION table, but the supplier product identifier is not stored in the sample schema database at all. You will alter the order entry schema to add this field and create a numerical value to serve as the current supplier product id. These changes can be considered a prerequisite to the changes instituted by your supplier.

• Once you have added an initial supplier product identifier for all the products you sell, you need to determine how you will add the modified product identifiers for this one supplier. You also need to have a method of controlling the effective date of the new identifiers.

• The purchasing department uses an inventory report to determine which products are getting low on stock. This report will need to reflect the current supplier product identifier until October 10, 2010. After that date, the report should print the new supplier product identifier so the purchasing agent can place and verify orders easily.

• The order entry system will continue to use your internal product identifier when orders are received from your customers. Orders and invoices must show your product identifier and name, plus the supplier product identifier.

• You have inventory on hand that is packaged with the current supplier product identifier. You can continue to sell those products as-is but your customer invoices must show the actual supplier product ID found on the packaging. This means your inventory system must treat the items labeled with the new numbering scheme as a distinct product.

As you make these changes, there are several basic tests and quality issues to consider. The points

that follow are not intended to be all-inclusive as every system will have its own unique test requirements; however, there are some quality checks that can be applied to all systems. You’ll use the following points as a starting point:

• All objects that were valid before your changes should be valid when your changes are complete. Views, functions, procedures, and packages can be invalidated by a table change, depending on how the code for those objects was originally written. You will check for invalid schema objects both before and after you make your changes. Objects that are invalidated as an indirect result of your planned modifications should recompile successfully without further changes.

Page 77: Managing SQL Performance

CHAPTER 15 ■ TESTING AND QUALITY ASSURANCE

467

• All data changes and results output must be accurate. Verifying data can be one of the more tedious tasks when developing or altering a database application, and the more data in the system, the more tedious the work will be. It's also the most critical test your code must past: if the data is not accurate, it doesn't matter if the other requirements have been met or how fast the code executes. If the data being stored or returned cannot be trusted, your code is wrong and you’ve failed the most basic requirement. The simplest approach is to break data verification into manageable components, beginning by verifying the core data set, and then gradually expanding the test to the more unique use cases (the “edge cases”) until you are certain all data is correct.

• Query performance can be verified by comparing the before and after versions of the execution plan. If the execution plan indicates the process has to work harder after your modifications, you want to be sure that additional work is, in fact, required and not the result of a mistake. Use of execution plans was addressed in detail in Chapter 6 so refer back to that chapter for more information on the topic plus tips on making the best use of the information found in the execution plan.

Later in this chapter, I will discuss code instrumentation and the Instrumentation Library for Oracle

(ILO). ILO uses Oracle’s DBMS_APPLICATION_INFO procedures. While it is possible to use the DBMS_APPLICATION_INFO procedures on their own, ILO makes it very easy and straightforward to add instrumentation to your code. I've added some additional functionality to the ILO package; the updates are available for download at Apress. Once your code is instrumented, these additional modules make it possible to build test systems that record processing times as you make iterative changes to your code or system configuration. This performance data will make it very clear when your changes have had a positive impact on processing times and when you might want to consider another approach.

Testing Methods There are as many different approaches to software testing as there are software development—and there have quite possibly been an equal number of battles fought over both topics. Although it may be slightly controversial in a database environment, I'm going to advocate an approach known as Test Driven Development (TDD). Test Driven Development originated in the realm of extreme programming so you will need to make some modifications to the process to make it effective for database development, but it offers some very genuine benefits for both new development and modification efforts.

In TDD, the developer begins by creating simple, repeatable tests that will fail in the existing system but will succeed once the change is implemented correctly. This approach has the following benefits:

• In order to write the test that will fail, you will have to thoroughly understand the requirements and the current implementation before you even begin to write application code.

• Building the unit test script first ensures that you start by working through the logic of the necessary changes, thereby decreasing the odds that your code will have bugs or need a major rewrite.

• By creating small, reusable unit tests to verify the successful implementation of a change, you build a library of test scripts that can be used both to test the initial implementation of the change and to confirm that the feature is still operating as expected as other system changes are made.

• These small unit test scripts can be combined to create larger integration-testing scripts or become part of an automated test harness to simplify repetitive testing.

Page 78: Managing SQL Performance

CHAPTER 15 ■ TESTING AND QUALITY ASSURANCE

468

• TDD assists in breaking changes or new development into smaller, logical units of work. The subsets may be easier to understand and explain to other developers, which can be especially important when project members are not co-located.

• When test design is delayed until after development, testing frequently ends up being shortchanged, with incomplete or poorly written tests due to schedule constraints. Including test development efforts in the code development phases results in higher quality tests that are more accurate.

As acknowledged earlier, TDD needs some adjustments in a database environment or you run the

risk of building yet another black box database application that is bound to fail performance and scalability testing. Whenever you are developing or modifying an application that stores or retrieves information from a database, as you are preparing those first unit tests, you must consider the data model or work with the individual(s) responsible for that task. In my (sometimes) humble opinion, the data model is the single most important indicator of a database application’s potential to perform. The schema design is crucial to the application’s ability to scale for more users, more data, or both. This does not mean that development cannot begin until there is a flawless entity-relationship model, but it does mean that the core data elements must be understood and the application tables should be well designed for at least those core elements. And if the database model is not fully developed, then build the application using code that will not result in extensive changes as the data model is refactored.

So what exactly am I suggesting? To put it bluntly, if your application schema will continue to be developed progressively, use procedures and packages for your application code. This will allow the database to be refactored as data elements are moved or added, without requiring major front end code rewrites.

■NOTE This has been far from a complete explanation of Test Driven Development or database refactoring. I strongly recommend the book Refactoring Databases: Evolutionary Database Design by Scott W. Ambler and Pramodkumar J. Sadalage for a look at database development using Agile methods. If you are interested in information specifically on TDD, you are welcome to contact me directly for additional references.

But let’s get back to your application changes, shall we? In the case of the changing supplier product identifier, you begin by asking some questions. How will this new data element be used by your company and your employees? How will this change impact your order entry and inventory data? Will this change impact systems or processes beyond your order entry system? At minimum, your purchasing agents need the supplier's current product identifier to place an order for new products. Depending on how well recognized the component is, the supplier’s product identifier could be used more widely than one might expect. A specific product or component may even be a selling point with your customers. A great example is CPUs: the make and the model of the processor in the laptop can be far more important than the brand name on the case. If this is true for the products you are reselling, the supplier's product id may be represented throughout multiple systems beyond the ordering system, so it would be necessary to extend your evaluation to include additional systems and processes.

Unit Tests As noted in the previous section, your first goal will be to write the unit tests you need to demonstrate that your application modifications are successful. However, since this is a database application, you need to determine where this data element belongs before you can even begin to write the first unit test. Although the Oracle-provided sample schemas are far from perfect, you cannot refactor the entire

Page 79: Managing SQL Performance

CHAPTER 15 ■ TESTING AND QUALITY ASSURANCE

469

schema in this chapter, so there will be many data design compromises in the examples. This can also be true in the real world: it is seldom possible to make all the corrections we know should be made. This is why correcting problems in the schema design can be a long, iterative process requiring very careful management.

■NOTE Reminder: The focus for this chapter is testing methods. I’ll keep the examples as short as possible to avoid detracting from the core message. This means the examples do not represent production ready code, nor do the sample schemas represent production ready data models.

Considering the primary Order Entry functions that will make use of the supplier product identifier, you decide to store the supplier product id in the PRODUCT_INFORMATION table. This table contains other descriptive attributes about the product, and it is already used in the output reports that will now need to include your newest data element. These are not the sole considerations when deciding where and how to store data, but for your purposes in this chapter, it will do. In the real world, the amount of data to be stored and accessed, which data values will read most frequently, and how often specific data values will be updated should all be considered prior to making decisions about where the data belongs.

Once you’ve decided where you will keep the data, you can begin preparing the necessary unit tests for your change. So, what are the unit tests that will fail before you’ve added the supplier’s product id to your schema? Here’s a list of the unit tests you will complete in this chapter:

• Include the supplier’s product id on individual orders and invoices.

• Print the supplier’s product id on the open order summary report.

• Print a purchasing report that shows the current supplier’s product id. If you have been using a TDD process throughout development, then there are likely to be several

generic unit tests that have already been written and may be appropriate to include in this round of tests. Typical verification tests may focus on the following tasks:

• Confirm that all objects are valid before and after your changes.

• Confirm that an insert will fail if required constraints are not met.

• Verify that default values are populated when new data records are added.

• Execute a new order transaction with and without products from this specific supplier. If you have been thorough in your initial evaluation and unit test development work, you will know

which tests are expected to fail. Other operations, such as the new order transaction I covered in the last chapter, you would expect to succeed, as you did not note that any changes are required for a new order. Should the existing unit tests for creating a new order fail after your changes, it would indicate that you did not analyze the impact of this latest change as thoroughly as you should have.

Before you make any changes to the database objects, you should confirm the state of the existing objects. Preferably, all objects will be valid before you start making changes. This is important as it ensures that you are aware of any objects that were invalid prior to your changes, and it helps you to recognize when you are responsible for invalidating the objects. Listing 15-1 shows a query to check for invalid objects and the result of the query.

Page 80: Managing SQL Performance

CHAPTER 15 ■ TESTING AND QUALITY ASSURANCE

470

Listing 15-1. Checking for Invalid Objects Prior to Altering Database Objects

SQL> select object_name, object_type, last_ddl_time, status from user_objects where status != 'VALID'; no rows selected

Listing 15-2 shows your three unit test scripts. Each of these scripts represents a report that must include the correct supplier product identifier as related to your internal product number. The first test creates a report for a single order, which is essentially the customer’s invoice. The second test is the purchasing report, which must print the correct supplier product identifier plus the inventory on hand. The third unit test is a complete listing of all open orders; it has been built using several views.

Listing 15-2. Unit Test Scripts

--- order_report.sql set linesize 115 column order_id new_value v_order noprint column order_date new_value v_o_date noprint column line_no format 99 column order_total format 999,999,999.99 BREAK ON order_id SKIP 2 PAGE BTITLE OFF compute sum of line_item_total on order_id ttitle left 'Order ID: ' v_order - right 'Order Date: ' v_o_date - skip 2 spool logs/order_report.txt select h.order_id ORDER_ID, h.order_date, li.line_item_id LINE_NO, li.supplier_product_id SUPP_PROD_ID, li.product_name, li.unit_price, li.discount_price, li.quantity, li.line_item_total from order_detail_header h, order_detail_line_items li where h.order_id = li.order_id and h.order_id = ‘&Order_Number’ order by h.order_id, line_item_id ; spool off --- purchasing_report.sql

Page 81: Managing SQL Performance

CHAPTER 15 ■ TESTING AND QUALITY ASSURANCE

471

break on supplier skip 1 column target_price format 999,999.99 set termout off spool logs/purchasing_report.txt select p.supplier_id SUPPLIER, p.supplier_product_id SUPP_PROD_ID, p.product_name PRODUCT_NAME, i.quantity_on_hand QTY_ON_HAND, (p.min_price * .5) TARGET_PRICE from product_information p, inventories i where p.product_id = i.product_id and p.product_status = 'orderable' and i.quantity_on_hand < 1000 order by p.supplier_id, p.supplier_product_id ; spool off set termout on --- order_reports_all.sql set linesize 115 column order_id new_value v_order noprint column order_date new_value v_o_date noprint column line_no format 99 column order_total format 999,999,999.99 BREAK ON order_id SKIP 2 PAGE BTITLE OFF compute sum of line_item_total on order_id ttitle left 'Order ID: ' v_order - right 'Order Date: ' v_o_date - skip 2 select h.order_id ORDER_ID, h.order_date, li.line_item_id line_no, li.product_name, li.supplier_product_id ITEM_NO, li.unit_price, li.discount_price, li.quantity, li.line_item_total from order_detail_header h, order_detail_line_item li where h.order_id = li.order_id order by h.order_id, li.line_item_id ;

Listing 15-3 shows execution of your unit test scripts and the resulting (expected) failures.

Page 82: Managing SQL Performance

CHAPTER 15 ■ TESTING AND QUALITY ASSURANCE

472

Listing 15-3. Initial Unit Test Results

SQL> @ order_report.sql li.supplier_product_id, * ERROR at line 2: ORA-00904: "LI"."SUPPLIER_PRODUCT_ID": invalid identifier SQL> @purchasing_report.sql order by p.supplier_id, p.supplier_product_id * ERROR at line 6: ORA-00904: "P"."SUPPLIER_PRODUCT_ID": invalid identifier SQL> @order_report_all.sql li.line_item_id line_no, li.product_name, li.supplier_product_id ITEM_NO, * ERROR at line 2: ORA-00904: "LI"."SUPPLIER_PRODUCT_ID": invalid identifier

Unit tests are typically created for and executed from the application interface but it’s extremely

helpful to create database-only unit tests as well. Having a set of scripts that you can run independently of the application code outside of the database will allow you to check database functionality before you hand new code over to the test team. And if the front-end application tests result in unexpected errors, you will already have information about a successful database level execution, which will help both teams troubleshoot problems more efficiently.

Regression Tests The goal of regression testing is to confirm that all prior functionality continues to work as expected. You also must be certain that you do not re-introduce old issues (bugs) into your code as you implement new functionality. Regression tests are most likely to fail when there has not been adequate source code control so someone has inadvertently used an obsolete piece of code as their starting point.

If unit tests were written for the existing functionality as the first step when the functionality was developed, those unit tests become the regression tests to confirm that each component of the system is still working as expected. In your case, the tests used to verify the order transaction process can be used to verify that orders will still be processed as expected. Although I’m cheating a bit, I’ll skip the re-execution of the order entry transactions as I spent many pages on this topic in the last chapter.

Schema Changes As a prerequisite to executing your examples, you need to make several changes to your schema to support storing a supplier product number at all. You’ll add a new varchar2 column in the PRODUCT_INFORMATION table to store the SUPPLIER_PRODUCT_ID field for each item you sell. You’ll populate the new column with a value to represent the current supplier product ids for all the

Page 83: Managing SQL Performance

CHAPTER 15 ■ TESTING AND QUALITY ASSURANCE

473

products you sell, and you’ll use the DBMS_RANDOM package to generate these numbers. Once this data exists, your basic unit tests referencing the supplier product identifier should succeed.

However, to support the concept of effective product ids, you will add new records to the PRODUCT_INFORMATION table using your supplier’s new identification values, a new internal product number with the same product description and pricing. While you could update the existing records, this would violate the requirement to accurately reflect the supplier’s product identifier shown on the product packaging in your warehouses. It would also result in changing historical data, since you’ve already sold copies of this software to other customers. Although the software in the package is unchanged, the fact that your supplier has relabeled it essentially creates a brand new product, which is why you need these new product records. The new records will be entered with a product status of “planned,” since the effective date is in the future. On the October 10, 2010, the new parts will be marked as “orderable” and the current parts will become “obsolete.”

In order to manage the effective dates for the changing internal product identifiers, you will create a new table, PRODUCT_ID_EFFECTIVITY. You’ll also create a PRODUCT_ID sequence to generate your new internal identifiers, making certain that your sequence begins at a higher value that any of your existing product records. Although I won’t cover it in this chapter, this table could be used by a scheduled process that would update the PRODUCT_STATUS field in the PRODUCT_INFORMATION table to reflect whether a product was planned, orderable, or obsolete. It is the change in product status that will trigger which supplier’s product id is shown on the purchasing report so the purchasing agent can reference the correct number when placing new orders. Listing 15-4 shows the schema changes as they are processed.

Listing 15-4. Schema Changes and New Product Data

SQL> alter table product_information add supplier_product_id varchar2(15); Table altered. SQL> update product_information set supplier_product_id = round(dbms_random.value(100000, 80984),0) ; 288 rows updated. SQL> commit; Commit complete. SQL> create sequence product_id start with 3525 ; Sequence created. SQL> create table product_id_effectivity ( product_id number, new_product_id number, supplier_product_id varchar(15), effective_date date) ; Table created. SQL> insert into product_id_effectivity

Page 84: Managing SQL Performance

CHAPTER 15 ■ TESTING AND QUALITY ASSURANCE

474

(select product_id, product_id.nextval, round(dbms_random.value(100000, 80984),0)||'-'|| substr(product_name, instr(product_name,'/',-1,1)+1), '10-oct-10' from product_information, dual where supplier_id = 103089 and product_name like '%/%') ;

9 rows created.

SQL> select * from product_id_effectivity ;

PRODUCT_ID NEW_PRODUCT_ID SUPPLIER_PRODUC EFFECTIVE_DATE ---------- -------------- --------------- ------------------- 3170 3525 93206-SP 0010-10-10 00:00:00 3171 3526 84306-EN 0010-10-10 00:00:00 3176 3527 89127-EN 0010-10-10 00:00:00 3177 3528 81889-FR 0010-10-10 00:00:00 3245 3529 96987-FR 0010-10-10 00:00:00 3246 3530 96831-SP 0010-10-10 00:00:00 3247 3531 85011-DE 0010-10-10 00:00:00 3248 3532 88474-DE 0010-10-10 00:00:00 3253 3533 82876-EN 0010-10-10 00:00:00

9 rows selected.

SQL> commit ;

Commit complete.

SQL> insert into product_information ( product_id, product_name, product_description, category_id, weight_class, supplier_id, product_status, list_price, min_price, catalog_url, supplier_product_id) (select e.new_product_id, p.product_name, p.product_description, p.category_id, p.weight_class, p.supplier_id, 'planned', p.list_price, p.min_price, p.catalog_url, e.supplier_product_id

Page 85: Managing SQL Performance

CHAPTER 15 ■ TESTING AND QUALITY ASSURANCE

475

from product_information p, product_id_effectivity e where p.product_id = e.product_id and p.supplier_id = 103089) ;

9 rows created. SQL> select product_id, product_name, product_status, supplier_product_id from product_information where supplier_id = 103089 order by product_id ; PRODUCT_ID PRODUCT_NAME PRODUCT_STATUS SUPPLIER_PRODUC ---------- --------------------------------- -------------------- --------------- 3150 Card Holder - 25 orderable 3150 3170 Smart Suite - V/SP orderable 3170 3171 Smart Suite - S3.3/EN orderable 3171 3175 Project Management - S4.0 orderable 3175 3176 Smart Suite - V/EN orderable 3176 3177 Smart Suite - V/FR orderable 3177 3245 Smart Suite - S4.0/FR orderable 3245 3246 Smart Suite - S4.0/SP orderable 3246 3247 Smart Suite - V/DE orderable 3247 3248 Smart Suite - S4.0/DE orderable 3248 3253 Smart Suite - S4.0/EN orderable 3253 3525 Smart Suite - V/SP planned 93206-SP 3526 Smart Suite - S3.3/EN planned 84306-EN 3527 Smart Suite - V/EN planned 89127-EN 3528 Smart Suite - V/FR planned 81889-FR 3529 Smart Suite - S4.0/FR planned 96987-FR 3530 Smart Suite - S4.0/SP planned 96831-SP 3531 Smart Suite - V/DE planned 85011-DE 3532 Smart Suite - S4.0/DE planned 88474-DE 3533 Smart Suite - S4.0/EN planned 82876-EN 20 rows selected.

Once you’ve completed the necessary schema updates, your next step will be to check for invalid objects again. All objects were valid when you ran your initial check, but now you have altered a table that is likely to be referenced by several other code objects in your schema. If those objects were coded properly, you will be able to recompile them as-is and they’ll become valid again. If your code is sloppy (perhaps someone used a ‘select * from PRODUCT_INFORMATION’ clause to populate an object that does not have the new field), then the recompile will fail and you’ll need to plan for more application modifications. The unit test to look for invalid objects, plus the two recompiles that are required after your changes are shown in Listing 15-5.

Page 86: Managing SQL Performance

CHAPTER 15 ■ TESTING AND QUALITY ASSURANCE

476

Listing 15-5. Invalid Objects Unit Test and Object Recompile

SQL> select object_name, object_type, last_ddl_time, status from user_objects where status != 'VALID'; OBJECT_NAME OBJECT_TYPE LAST_DDL_ STATUS ----------------------------------- ------------------- --------- ------- GET_ORDER_TOTAL PROCEDURE 04-jul-10 INVALID GET_LISTPRICE FUNCTION 04-jul-10 INVALID SQL> alter function GET_LISTPRICE compile ; Function altered. SQL> alter procedure GET_ORDER_TOTAL compile ; Procedure altered. SQL> select object_name, object_type, last_ddl_time, status from user_objects where status != 'VALID'; no rows selected

Repeating the Unit Tests Once you’ve confirmed that your planned schema changes have been successfully implemented and all objects are valid, it’s time to repeat the remaining unit tests. This time, each of the tests should execute and you should be able to verify that the supplier’s product id is accurately represented in the data results. Results from the second execution of the unit test are shown in Listing 15-6. To minimize the number of trees required to print this book, output from the reports will be abbreviated.

Listing 15-6. Second Execution of Unit Tests

SQL> @order_report Order ID:5041 Order Date: 13 Jul 2010 NO SUP_PROD_ID PRODUCT_NAME UNIT_PRICE DISC_PRICE QTY ITEM_TOTAL --- ----------- ------------------------- ---------- ---------- ---- ---------- 1 98811 Smart Suite - S4.0/DE 222.00 199.80 5 999.00 SQL> @purchasing_report

Page 87: Managing SQL Performance

CHAPTER 15 ■ TESTING AND QUALITY ASSURANCE

477

SUPPLIER S_PRODUCT PRODUCT_NAME QTY_ON_HAND TARGET_PRICE ---------- ------------ ----------------------- ----------- ------------ 103086 96102 IC Browser Doc - S 623 50.00 103088 83069 OSI 1-4/IL 76 36.00 103089 86151 Smart Suite - S4.0/EN 784 94.00 89514 Smart Suite - V/DE 290 48.00 92539 Smart Suite - V/EN 414 51.50 93275 Smart Suite - V/FR 637 51.00 95024 Smart Suite - S4.0/SP 271 96.50 95857 Smart Suite - V/SP 621 66.00 98796 Smart Suite - S3.3/EN 689 60.00 98811 Smart Suite - S4.0/DE 114 96.50 99603 Smart Suite - S4.0/FR 847 97.50 ....... SQL> @order_report_all.sql Order ID: 2354 Order Date: 14 Jul 2002 ID PRODUCT_NAME ITEM_NO UNIT_PRICE DISCOUNT_PRICE QTY LINE_ITEM_TOTAL --- ------------------------ -------- ---------- -------------- ----- --------------- 1 KB 101/EN 94979 48.00 45.00 61 2,745.00 1 KB 101/EN 98993 48.00 45.00 61 2,745.00 1 KB 101/EN 85501 48.00 45.00 61 2,745.00 ....... Order ID: 5016 Order Date: 06 Jul 2010 ID PRODUCT_NAME ITEM_NO UNIT_PRICE DISCOUNT_PRICE QTY LINE_ITEM_TOTAL --- ------------------------ -------- ---------- -------------- ----- --------------- 1 Inkvisible Pens 86030 6.00 5.40 1000 5,400.00 Order ID: 5017 Order Date: 06 Jul 2010 ID PRODUCT_NAME ITEM_NO UNIT_PRICE DISCOUNT_PRICE QTY LINE_ITEM_TOTAL --- ------------------------ -------- ---------- -------------- ----- --------------- 1 Compact 400/DQ 87690 125.00 118.75 25 2,968.75 Order ID: 5041 Order Date: 13 Jul 2010 ID PRODUCT_NAME ITEM_NO UNIT_PRICE DISCOUNT_PRICE QTY LINE_ITEM_TOTAL --- ------------------------ -------- ---------- -------------- ----- --------------- 1 Smart Suite - S4.0/DE 98811 222.00 199.80 5 999.00

Take note that in each case where the product name shows a product that will be affected by your supplier’s new identifiers, your reports are still showing the current supplier identifier. That’s because these reports have all been executed as of a date prior to the October 10, 2010 effective date. What you have not yet addressed in your testing is a mechanism to set products referencing the old supplier product identifiers to “obsolete” and to make your new products referencing the new supplier product identifier “orderable.” After the effective date has passed, you need the purchasing report in particular

Page 88: Managing SQL Performance

CHAPTER 15 ■ TESTING AND QUALITY ASSURANCE

478

to reference the new IDs. Order data should continue to represent the item ordered and shipped, which would not necessarily be determined by the effective date for the part number change. Instead, you want your sales team to sell the older product first, so you would only begin to see the new product identifiers on orders and invoices after the existing inventory was depleted. This thought process should trigger the development of a few more unit tests, such as testing the process to alter product status after a product change effectivity date had passed and confirming that the Order Entry system will not make the new product identifiers available for purchase until the old stock has been depleted.

Execution Plan Comparison One of the best tools available for evaluating the impact of the changes you make to database objects and code is the execution plan. By recording the execution plan both before and after your changes, you have a detailed measurement of exactly how much work the database needs to complete in order to process requests for the data in the past and how much work will be required to process those same requests in the future. If the comparison of the before and after versions of the execution plan indicates that a significant amount of additional work is required, it may be necessary to reevaluate the code to see if you can optimize it. If you find the process is already as optimized as it can be, you can then use the information to nicely explain to the users that their report may take longer in the future due to the additional functionality. Once you express your findings in those terms, you will discover exactly how much the users value that new functionality, and it will be up to them to decide if the changes are important enough to move to production.

Comparing the execution plans can also make it very clear when there is something wrong with a query. If you find that a process is working much harder to get the data, but the new changes don’t justify the additional work, there is a strong possibility that there is an error in the code somewhere.

For the next example, you will review the execution plans of the complete order report from your unit testing. The execution plan recorded before you made any changes to the database is shown in Listing 15-7. The scripts to gather the execution plans are based on the approach demonstrated in Chapter 6.

Listing 15-7. Order Report Execution Plan (Before)

alter session set statistics_level = 'ALL'; set linesize 105 column order_id new_value v_order noprint column order_date new_value v_o_date noprint column ID format 99 column order_total format 999,999,999.99 BREAK ON order_id SKIP 2 PAGE BTITLE OFF compute sum of line_item_total on order_id ttitle left 'Order ID: ' v_order - right 'Order Date: ' v_o_date - skip 2 spool logs/order_report_all_pre.txt

Page 89: Managing SQL Performance

CHAPTER 15 ■ TESTING AND QUALITY ASSURANCE

479

select /* OrdersPreChange */ h.order_id ORDER_ID, order_date, li.line_item_id ID, li.product_name, li.product_id ITEM_NO, li.unit_price, li.discount_price, li.quantity, li.line_item_total from order_detail_header h, order_detail_line_items li where h.order_id = li.order_id order by h.order_id, li.line_item_id ; spool off set lines 150 spool logs/OrdersPreChange.txt @pln.sql OrdersPreChange PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------------------ SQL_ID ayucrh1mf6v4s, child number 0 ------------------------------------- select /* OrdersPreChange */ h.order_id ORDER_ID, order_date, li.line_item_id ID, li.product_name, li.product_id ITEM_NO, li.unit_price, li.discount_price, li.quantity, li.line_item_total from order_detail_header h, order_detail_line_items li where h.order_id = li.order_id order by h.order_id, li.line_item_id Plan hash value: 3662678147 ------------------------------------------------------------------------------------------- | Id |Operation |Name |Starts |E-Rows |A-Rows |Buffers | ------------------------------------------------------------------------------------------- | 0 |SELECT STATEMENT | | 1 | | 417 | 29 | | 1 | SORT ORDER BY | | 1 | 474 | 417 | 29 | |* 2 | HASH JOIN | | 1 | 474 | 417 | 29 | | 3 | TABLE ACCESS FULL |PRODUCT_INFORMATION| 1 | 297 | 297 | 16 | | 4 | NESTED LOOPS | | 1 | 474 | 417 | 13 | | 5 | MERGE JOIN | | 1 | 474 | 417 | 9 | |* 6 | TABLE ACCESS BY INDEX ROW|ORDERS | 1 | 79 | 79 | 2 | | 7 | INDEX FULL SCAN |ORDER_PK | 1 | 114 | 114 | 1 | |* 8 | SORT JOIN | | 79 | 678 | 417 | 7 | | 9 | TABLE ACCESS FULL |ORDER_ITEMS | 1 | 678 | 678 | 7 | |* 10 | INDEX UNIQUE SCAN |ORDER_STATUS_PK | 417 | 1 | 417 | 4 | ------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("OI"."PRODUCT_ID"="PI"."PRODUCT_ID") 6 - filter("O"."SALES_REP_ID" IS NOT NULL)

Page 90: Managing SQL Performance

CHAPTER 15 ■ TESTING AND QUALITY ASSURANCE

480

8 - access("O"."ORDER_ID"="OI"."ORDER_ID") filter("O"."ORDER_ID"="OI"."ORDER_ID") 10 - access("O"."ORDER_STATUS"="OS"."ORDER_STATUS") 35 rows selected.

The order report is generated by joining two views: the order header information and the order line item details. You’ll assume the report is currently running fast enough to meet user requirements and that there are no indicators that the quantity of data in the underlying tables is expected to increase dramatically in the future. The report is deemed as meeting requirements and the execution plan shall be saved for future reference.

This order report was executed as one of your first unit tests to verify that your unit tests work as expected. After you made the required database changes, you executed the order report again and confirmed that it completed. The report also seems to complete in about the same amount of time as it did in the past. But let’s take a look at the latest execution plan to see how the report is really performing. The post-change execution plan is shown in Listing 15-8.

Lising 15-8. Order Report Execution Plan (After)

alter session set statistics_level = 'ALL'; set linesize 115 column order_id new_value v_order noprint column order_date new_value v_o_date noprint column ID format 99 column order_total format 999,999,999.99 BREAK ON order_id SKIP 2 PAGE BTITLE OFF compute sum of line_item_total on order_id ttitle left 'Order ID: ' v_order - right 'Order Date: ' v_o_date - skip 2 spool logs/order_report_all_fail.txt select /* OrdersChangeFail */ h.order_id ORDER_ID, order_date, li.line_item_id ID, li.product_name, p.supplier_product_id ITEM_NO, li.unit_price, li.discount_price, li.quantity, li.line_item_total from order_detail_header h, order_detail_line_items li, product_information p where h.order_id = li.order_id and li.product_id = p.product_id order by h.order_id, li.line_item_id ; spool off set lines 150

Page 91: Managing SQL Performance

CHAPTER 15 ■ TESTING AND QUALITY ASSURANCE

481

spool logs/OrdersChangeFail.log @pln.sql OrdersChangeFail PLAN_TABLE_OUTPUT ------------------------------------------------------------------------------------------- SQL_ID avhuxuj0d23kc, child number 0 ------------------------------------- select /* OrdersChangeFail */ h.order_id ORDER_ID, order_date, li.line_item_id ID, li.product_name, p.supplier_product_id ITEM_NO, li.unit_price, li.discount_price, li.quantity, li.line_item_total from order_detail_header h, order_detail_line_items li, product_information p where h.order_id = li.order_id and li.product_id = p.product_id order by h.order_id, li.line_item_id Plan hash value: 1984333101 ------------------------------------------------------------------------------------------- | Id |Operation |Name |Starts |E-Rows |A-Rows |Buffers | ------------------------------------------------------------------------------------------- | 0 |SELECT STATEMENT | | 1 | | 417 | 45 | | 1 | SORT ORDER BY | | 1 | 474 | 417 | 45 | |* 2 | HASH JOIN | | 1 | 474 | 417 | 45 | | 3 | TABLE ACCESS FULL |PRODUCT_INFORMATION| 1 | 297 | 297 | 16 | |* 4 | HASH JOIN | | 1 | 474 | 417 | 29 | | 5 | TABLE ACCESS FULL |PRODUCT_INFORMATION| 1 | 297 | 297 | 16 | | 6 | NESTED LOOPS | | 1 | 474 | 417 | 13 | | 7 | MERGE JOIN | | 1 | 474 | 417 | 9 | |* 8 | TABLE ACCESS BY INDEX RO|ORDERS | 1 | 79 | 79 | 2 | | 9 | INDEX FULL SCAN |ORDER_PK | 1 | 114 | 114 | 1 | |* 10 | SORT JOIN | | 79 | 678 | 417 | 7 | | 11 | TABLE ACCESS FULL |ORDER_ITEMS | 1 | 678 | 678 | 7 | |* 12 | INDEX UNIQUE SCAN |ORDER_STATUS_PK | 417 | 1 | 417 | 4 | ------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("PI"."PRODUCT_ID"="P"."PRODUCT_ID") 4 - access("OI"."PRODUCT_ID"="PI"."PRODUCT_ID") 8 - filter("O"."SALES_REP_ID" IS NOT NULL) 10 - access("O"."ORDER_ID"="OI"."ORDER_ID") filter("O"."ORDER_ID"="OI"."ORDER_ID") 12 - access("O"."ORDER_STATUS"="OS"."ORDER_STATUS") 39 rows selected.

Page 92: Managing SQL Performance

CHAPTER 15 ■ TESTING AND QUALITY ASSURANCE

482

Looking at this latest plan, the database is doing much more work after your changes, even though the report is not taking any appreciable amount of extra time to complete. You know there is no good reason for this to be so: you’ve only added one additional column to a table that was already the central component of the query. Furthermore, the table in question already required a full table scan, as most of the columns are needed for the report. But the execution plan shows that your report is now doing two full table scans of the PRODUCT_INFORMATION table. Why?

In this case, I’ve made a common error deliberately to illustrate how an execution plan can help find quality problems in changed code. Rather than simply add the new column to the existing ORDER_DETAIL_LINE_ITEM view that is built on the PRODUCT_INFORMATION table, the PRODUCT_INFORMATION table has been joined to the ORDER_DETAIL_LINE_ITEM view, resulting in a second full table scan of the central table.

This probably seems like a really foolish mistake to make, but it can be easily done. I’ve seen many developers add a new column to a query by adding a new join to a table or view that was already part of the existing report. This error will have a clear and visible impact on an execution plan, especially if the query is complex (and it usually is when this type of error is made). Listing 15-9 shows the execution plan for the same query once the additional join is removed and the column is added to the existing ORDER_DETAIL_LINE_ITEM view instead.

Listing 15-9. Order Report Execution Plan (Corrected)

alter session set statistics_level = 'ALL'; set linesize 115 column order_id new_value v_order noprint column order_date new_value v_o_date noprint column ID format 99 column order_total format 999,999,999.99 BREAK ON order_id SKIP 2 PAGE BTITLE OFF compute sum of line_item_total on order_id ttitle left 'Order ID: ' v_order - right 'Order Date: ' v_o_date - skip 2 spool logs/order_report_all_corrected.txt select /* OrdersCorrected */ h.order_id ORDER_ID, order_date, li.line_item_id ID, li.product_name, li.supplier_product_id ITEM_NO, li.unit_price, li.discount_price, li.quantity, li.line_item_total from order_detail_header h, order_detail_line_items li where h.order_id = li.order_id order by h.order_id, li.line_item_id ; spool off set lines 150

Page 93: Managing SQL Performance

CHAPTER 15 ■ TESTING AND QUALITY ASSURANCE

483

spool logs/OrdersCorrected_plan.txt @pln.sql OrdersCorrected PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------------------- SQL_ID 901nkw7f6fg4r, child number 0 ------------------------------------- select /* OrdersCorrected */ h.order_id ORDER_ID, order_date, li.line_item_id ID, li.product_name, li.supplier_product_id ITEM_NO, li.unit_price, li.discount_price, li.quantity, li.line_item_total from order_detail_header h, order_detail_line_items li where h.order_id = li.order_id order by h.order_id, li.line_item_id Plan hash value: 3662678147 ------------------------------------------------------------------------------------------- | Id |Operation |Name |Starts |E-Rows |A-Rows |Buffers | ------------------------------------------------------------------------------------------- | 0 |SELECT STATEMENT | | 1| | 417 | 29 | | 1 | SORT ORDER BY | | 1| 474| 417 | 29 | |* 2 | HASH JOIN | | 1| 474| 417 | 29 | | 3 | TABLE ACCESS FULL |PRODUCT_INFORMATION| 1| 297| 297 | 16 | | 4 | NESTED LOOPS | | 1| 474| 417 | 13 | | 5 | MERGE JOIN | | 1| 474| 417 | 9 | |* 6 | TABLE ACCESS BY INDEX ROW|ORDERS | 1| 79| 79 | 2 | | 7 | INDEX FULL SCAN |ORDER_PK | 1| 114| 114 | 1 | |* 8 | SORT JOIN | | 79| 678| 417 | 7 | | 9 | TABLE ACCESS FULL |ORDER_ITEMS | 1| 678| 678 | 7 | |* 10 | INDEX UNIQUE SCAN |ORDER_STATUS_PK | 417| 1| 417 | 4 | ------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("OI"."PRODUCT_ID"="PI"."PRODUCT_ID") 6 - filter("O"."SALES_REP_ID" IS NOT NULL) 8 - access("O"."ORDER_ID"="OI"."ORDER_ID") filter("O"."ORDER_ID"="OI"."ORDER_ID") 10 - access("O"."ORDER_STATUS"="OS"."ORDER_STATUS") 35 rows selected.

You can see by this latest execution plan that your report is now performing as expected, with no

additional impact to performance or use of system resources.

Page 94: Managing SQL Performance

CHAPTER 15 ■ TESTING AND QUALITY ASSURANCE

484

Instrumentation One of my favorite Oracle features is instrumentation. The database itself is fully instrumented, which is why you can see exactly when the database is waiting and what it is waiting for. Without this instrumentation, a database would be something of a black box, providing little information about where resources are spending, or not spending, their time.

Oracle also provides the DBMS_APPLICATION_INFO package that you can use to instrument the code that you write. This package allows you to label the actions and modules within your code so that you can more easily identify which processes in your application are active. You can also combine your instrumentation data with Oracle’s Active Session History (ASH), Active Workload Repository (AWR), and other performance management tools to gain further insight into your application’s performance while easily filtering out other unrelated processes.

The simplest method I know of for adding instrumentation to application code is the Instrumentation Library for Oracle (ILO), which is available at http://sourceforge.net/projects/ilo/. ILO is open source software written and supported by my friends at Method-R. Method-R also offers the option to purchase a license for ILO so that it can be used in commercial software products. I’ve been using ILO to instrument code for several years and have added functionality to the 2.3 version. The enhancements allow me to record the exact start and stop time of an instrumented process using the database’s internal time references. This data can then be used to calculate statistical indicators on process execution times, which helps to highlight potential performance issues before they become major problems. I’ve also added code to enable 10044 tracing for a specific process by setting an On/Off switch in a table. So if I determine that I need trace data for a specific application process, I can set tracing to On for that process by its instrumented process name and it will be traced every time it executes until tracing is set to Off again. The configuration module can also be used to set the elapsed time collection On or Off, but I usually prefer to leave elapsed time recording on and purge older data when it is no longer useful.

If you’d like to test the ILO instrumentation software as you go through the next few sections, start by downloading ILO 2.3 from SourceForge.net and install it per the instructions. You can then download the code to store elapsed time and set the trace and timing configuration from the Apress download site. Instructions to add the updates are included in the ZIP file.

Adding Instrumentation to Code Once you’ve installed the ILO schema, adding instrumentation to your application is easily done. There are several ways to accomplish this. Of course, you’ll need to determine the best method and the appropriate configuration based on your environment and requirements, but here are a few general guidelines:

• Within your own session, you can turn timing and tracing on or off at any time. You can also instrument any of your SQL statements by executing the ILO call to begin a task before you execute your SQL statement and executing the call to end the task after the statement. This approach is shown in Listing 15-10.

• You can encapsulate your code within a procedure and include the calls to ILO within the procedure itself. This has the added advantage of ensuring that every call to the procedure is instrumented and that the ILO action and module are labeled consistently. Consistent labeling will be very important if you want to aggregate your timing data in a meaningful way, or track trends in performance. I’ll look at the billing.credit_request procedure from Chapter 14 with added calls to ILO in Listing 15-11.

• You can create an application-specific wrapper to call the ILO procedures. One benefit of using a wrapper is that you can make sure a failure in ILO does not result in a failure for the application process. While you do want good performance data, you don’t want to prevent the application from running because ILO isn’t working. A simple wrapper is included with the ILO update download at Apress.

Page 95: Managing SQL Performance

CHAPTER 15 ■ TESTING AND QUALITY ASSURANCE

485

Listing 15-10. ILO Execution in a Single Session

SQL> exec ilo_timer.set_mark_all_tasks_interesting(TRUE,TRUE); PL/SQL procedure successfully completed. SQL> exec ilo_task.begin_task('Month-end','Purchasing'); PL/SQL procedure successfully completed. SQL> @purchasing_report SQL> exec ilo_task.end_task; PL/SQL procedure successfully completed.

Selected from ILO_ELAPSED_TIME table:

INSTANCE: TEST SPID: 21509 ILO_MODULE: Month-end ILO_ACTION: Purchasing START_TIME: 14-JUL-10 06.08.19.000000 AM END_TIME: 14-JUL-10 06.09.06.072642 AM ELAPSED_TIME: 46.42 ELAPSED_CPUTIME: .01 ERROR_NUM: 0

Listing 15-11. Incorporating ILO into a Procedure

create or replace procedure credit_request(p_customer_id IN NUMBER, p_amount IN NUMBER, p_authorization OUT NUMBER, p_status_code OUT NUMBER, p_status_message OUT VARCHAR2)

IS

/****************************************************************************** status_code values status_code status_message =========== =========================================================== 0 Success -20105 Customer ID must have a non-null value. -20110 Requested amount must have a non-null value. -20500 Credit Request Declined. ******************************************************************************/

v_authorization NUMBER;

Page 96: Managing SQL Performance

CHAPTER 15 ■ TESTING AND QUALITY ASSURANCE

486

BEGIN ilo_task.begin_task('New Order', 'Credit Request'); SAVEPOINT RequestCredit; IF ( (p_customer_id) IS NULL ) THEN RAISE_APPLICATION_ERROR(-20105, 'Customer ID must have a non-null value.', TRUE); END IF; IF ( (p_amount) IS NULL ) THEN RAISE_APPLICATION_ERROR(-20110, 'Requested amount must have a non-null value.', TRUE); END IF; v_authorization := round(dbms_random.value(p_customer_id, p_amount), 0); IF ( v_authorization between 324 and 342 ) THEN RAISE_APPLICATION_ERROR(-20500, 'Credit Request Declined.', TRUE); END IF; p_authorization:= v_authorization; p_status_code:= 0; p_status_message:= NULL; ilo_task.end_task; EXCEPTION WHEN OTHERS THEN p_status_code:= SQLCODE; p_status_message:= SQLERRM; BEGIN ROLLBACK TO SAVEPOINT RequestCredit; EXCEPTION WHEN OTHERS THEN NULL; END; ilo_task.end_task(error_num => p_status_code); END credit_request; / Execution Script: set serveroutput on DECLARE P_CUSTOMER_ID NUMBER; P_AMOUNT NUMBER;

Page 97: Managing SQL Performance

CHAPTER 15 ■ TESTING AND QUALITY ASSURANCE

487

P_AUTHORIZATION NUMBER; P_STATUS_CODE NUMBER; P_STATUS_MESSAGE VARCHAR2(200); BEGIN P_CUSTOMER_ID := '&customer'; P_AMOUNT := '&amount'; billing.credit_request( P_CUSTOMER_ID => P_CUSTOMER_ID, P_AMOUNT => P_AMOUNT, P_AUTHORIZATION => P_AUTHORIZATION, P_STATUS_CODE => P_STATUS_CODE, P_STATUS_MESSAGE => P_STATUS_MESSAGE ); commit; DBMS_OUTPUT.PUT_LINE('P_CUSTOMER_ID = ' || P_CUSTOMER_ID); DBMS_OUTPUT.PUT_LINE('P_AMOUNT = ' || P_AMOUNT); DBMS_OUTPUT.PUT_LINE('P_AUTHORIZATION = ' || P_AUTHORIZATION); DBMS_OUTPUT.PUT_LINE('P_STATUS_CODE = ' || P_STATUS_CODE); DBMS_OUTPUT.PUT_LINE('P_STATUS_MESSAGE = ' || P_STATUS_MESSAGE); END; / Execution: SQL> @exec_CreditRequest Enter value for customer: 237 Enter value for amount: 10000 P_CUSTOMER_ID = 237 P_AMOUNT = 10000 P_AUTHORIZATION = 8302 P_STATUS_CODE = 0 P_STATUS_MESSAGE = PL/SQL procedure successfully completed. SQL> @exec_CreditRequest Enter value for customer: 334 Enter value for amount: 500 P_CUSTOMER_ID = 237 P_AMOUNT = 500 P_AUTHORIZATION =

Page 98: Managing SQL Performance

CHAPTER 15 ■ TESTING AND QUALITY ASSURANCE

488

P_STATUS_CODE = -20500 P_STATUS_MESSAGE = ORA-20500: Credit Request Declined. PL/SQL procedure successfully completed.

Selected from ILO_ELAPSED_TIME table: INSTANCE: TEST SPID: 3896 ILO_MODULE: New Order ILO_ACTION: Request Credit START_TIME: 14-JUL-10 01.43.41.000000 AM END_TIME: 14-JUL-10 01.43.41.587155 AM ELAPSED_TIME: .01 ELAPSED_CPUTIME: 0 ERROR_NUM: 0 The level of granularity you decide to implement with your instrumentation depends on your goals.

For some tasks, it will be perfectly acceptable to include multiple processes in a single ILO module or action. For critical code, I recommend that you instrument the individual processes with their own action and module values, which will give you more visibility into complex procedures. If you are supporting an application that is not instrumented and it seems like too big a task to go back and instrument all the existing code, consider adding the instrumentation just to the key processes.

Again, how you decide to implement will depend on your needs. Instrumentation is exceptionally useful for testing code and configuration changes during development and performance testing. Once the calls to ILO have been built into the code, you can turn timing/tracing on or off in production to provide definitive performance data. Overhead is exceedingly low and being able to enable tracing easily will help you find the problems much more quickly.

Using the ILO_ELAPSED_TIME table to store performance data will typically allow you to retain critical performance data for longer periods of time. While it is possible to set longer retention values for AWR data, some sites may not have the resources available to keep as much data as they would like. Since the ILO data is not part of the Oracle product itself, you have the option to customize the retention levels to your needs without endangering any Oracle delivered capabilities.

■NOTE Keep the ILO code in its own schema and allow other schemas to use the same code base. This will keep the instrumentation code and data consistent, which will allow you to roll performance data up to the server level or across other multiple servers when appropriate.

Testing for Performance Once you’ve added instrumentation to your code, you open the door to all kinds of potential uses for the instrumentation and the data you collect. Earlier in this chapter, I talked about building test harnesses by automating many small unit test scripts and then replaying those tasks to confirm that new and old functionality are working as expected and that old bugs have not been reintroduced. If your code is instrumented, you can record the timing for each execution of the test harness and you will have

Page 99: Managing SQL Performance

CHAPTER 15 ■ TESTING AND QUALITY ASSURANCE

489

complete information on the exact amount of elapsed time and CPU time required for each labeled module and action.

The ILO package includes an ILO_COMMENT field in addition to the ILO_MODULE and ILO_ACTION labels. In some cases, this field can be used to record some identifying piece of information about a specific process execution. For example, if you were to add instrumentation to the order transaction from the last chapter, you could record the order number in the ILO_COMMENT field. Then if you found an exceptionally long execution in your ILO_ELAPSED_TIME table, you could connect that execution time with an order number, which then connects you to a specific customer and a list of ordered items. Combining this information with the very specific timestamp recorded in your table can help you troubleshoot the problem, ensure the transaction did process correctly, and determine the cause of the unusually long execution time.

In other cases, you may want to use the comment field to label specific set of test results for future reference. When testing changes to an application or instance configuration, it’s always better to make one change and measure the results before making additional adjustments. Otherwise, how will you know which change was responsible for the results you obtained? This can be very difficult to do, unless you’ve created a test harness and measurement tool that can be easily and consistently re-executed multiple times. By making a single change, re-executing the complete test package while recording timing data, and labeling the results set of that test execution, you create a series of data sets, each showing the impact of a specific change, test dataset, or stress factor. Over time, this information can be used to evaluate the applications ability to perform under a wide range of conditions.

A sample of data retained from one such test harness is shown in Table 15-1 (times are shown in seconds).

Table 15-1. Repetitive Test Results

ILO ACTION COUNT MIN AVG MAX VAR

CPU

MIN

CPU

AVG

CPU

MAX

CPU

VAR

process 1 46 0 .01 .09 0 0 .008 .03 0

process 2 2 .12 .125 .13 0 .12 .125 .13 0

process 3 2772526 0 .382 4.44 .078 0 .379 2.6 .074

child 3a 2545208 .01 .335 2.26 .058 .01 .332 1.77 .055

child 3b 2752208 0 .065 2.24 .011 0 .065 1.39 .01

child 3c 2153988 0 0 .21 0 0 0 .02 0

child 3d 2153988 0 0 .36 0 0 0 .07 0

child 3e 2153988 0 0 .16 0 0 0 .02 0

child 3f 2153988 0 0 .42 0 0 0 .02 0

process 4 1564247 0 .001 .18 0 0 .001 .02 0

process 5 2873236 0 .043 6.2 .013 0 .041 .49 .006

process 6 149589 0 .018 5.53 .002 0 .013 .11 0

process 7 2395999 0 .001 6 0 0 .001 .03 0

Page 100: Managing SQL Performance

CHAPTER 15 ■ TESTING AND QUALITY ASSURANCE

490

While the numbers shown above aren’t particularly meaningful on their own, if you have this set of numbers representing code executions prior to a change and you have another set of numbers from the same server with the same data set representing code execution after the code has been changed, you have definitive information regarding the impact your code changes have had on the database. Imagine being able to quickly and painlessly repeat this test for subsequent code changes and configuration adjustments, and you’ll begin to appreciate the potential of code instrumentation combined with repeatable, automated test processes.

Testing to Destruction Testing a system to its breaking point can be one of the more entertaining aspects of software testing, and meetings to brainstorm all the possible ways to break the database are seldom dull. Early in my career, I developed and managed an Oracle database application built using client/server technology. (Yes, this was long ago and far away.) The application itself was a problem tracking tool that allowed manufacturing workers to record issues they found and send those problems to Engineering for review and correction. The initial report landed in Quality Engineering, where it would be investigated and assigned to the appropriate Engineering group. As each Engineering department signed off on their work, the request would move on to the next group. The application was reasonably successful so it ended up on many workstations through a very large facility.

If you ever want to see “testing to destruction” in action, try supporting a database application installed on the workstations of hundreds of electrical, hydraulic, and structural engineers. In a fairly short period of time, I learned that engineers will do everything in their power to learn about the computers on their desks, and they considered breaking those computers and the applications on them to be an educational experience. I can’t say that I disagree with them: sometimes taking something apart just so you can build it again really is the best way to understand the guts of the tool.

However, after several months of trying to support this very inquisitive group of people, I developed a new approach to discourage excessive tampering. By keeping a library of ghosted drives containing the standard workstation configuration with all the approved applications, I could replace the hard drive on a malfunctioning computer in under 10 minutes and the engineer and I could both get back to work. Since everyone was expected to store their work on the server, no one could really object to my repair method. However, most engineers did not like losing their customized desktops, so they soon quit trying quite so hard to break things.

Although I loved to grumble at those engineers, I really owe them a very big thank you, for now whenever I need to think about how to test a server or application to destruction, all I need to do is think about those engineers and wonder what they would do. And never discount even the craziest ideas: if you can think of it, someone is likely to try it. As you work to identify your system’s weak links, consider everything on the following list, and then think of some more items:

Data Entry: What happens when a user or program interface sends the wrong kind of data or too much data?

Task Sequences: What happens when a user skips a step or performs the tasks out of order?

Repeating/Simultaneous Executions: Can the user run the same process simultaneously? Will that action corrupt data or will it just slow the process down?

Unbounded Data Ranges: Can the user request an unreasonable amount of data? What happens if the user enters an end range that is prior to the start range (such as requesting a report on sales from July 1, 2010 to June 30, 2010)?

Resource usage: Excessive use of CPU, memory, temporary storage and undo space can impact many users at the same time. Your DBA should limit usage with resource caps appropriately, but you still need to identify all the potential ways users and processes can grab more than their fair share.

Page 101: Managing SQL Performance

CHAPTER 15 ■ TESTING AND QUALITY ASSURANCE

491

I bet some of you could add some very interesting options for other ways to break systems. Can you also identify the ways to prevent those problems? While finding the best correction is a bit harder and not as entertaining, every time you can make it difficult for a user to break your code, you create a more robust system—one that will need less support and less maintenance over the long run.

Every system will have its own weakest links. Once you’ve identified those weaknesses, assemble your unit tests into a test harness that will push that resource beyond its limits so you can see how the system responds. In general, it seems that memory and I/O usage are the primary stressors for a databases system. However, lately I’ve been working on an Oracle 11g database with spatial functionality, and in this case, CPU processing is the system’s bottleneck. When we designed the system capacity tests, we made certain that the spatial processes would be tested to the extreme and we measured the internal database performance using the ILO data as shown in the last section. We also had external measurements of the total application and system performance, but having the ILO elapsed time data provided some unique advantages over other test projects I’ve participated in.

First and foremost, the ILO data provides specific measurements of the time spent in the database. This makes it easier to troubleshoot performance issue that do show up, as you can quickly tell when the process is slow in the database and when it is not. A second advantage is that the recorded timestamps give a very specific indicator of exactly when a problem occurred, what other processes were running at the same time, and the specific sequencing of the application processes. With this information, you can easily identify the point when the system will hit the knee in its performance curve. And since the elapsed time module in ILO uses DBMS_UTILITY.get_time and DBMS_UTLITITY.get_cpu_time, you can record exactly how much time your process spent active in the database and what portion of that time was spent on CPU.

This detailed performance data is also useful for troubleshooting, as the low level timestamps assist in narrowing down the timeframe for the problem. Once you know the specific timeframe you need to research, you can review a much smaller quantity of AWR or StatsPack data to see what happened and find the answers quickly. Once the window is small enough, any problem will be almost immediately visible. I’ll look at a specific case in the next section.

Troubleshooting through Instrumentation Sometimes it can be difficult to identify the cause of small problems. When you don’t know the source of the problem, you also don’t know the potential impact the problem could have on your application. In one such case, developers had noticed timeouts from the database at random intervals, yet the process they suspected of causing the issue showed no sign of the errors and the database appeared to be working well below its potential.

About a week after a new test server was installed, a review of the ILO_ELAPSED_TIME table showed that most tasks were performing well, except there were two processes that had overrun the 30 second timeout clock on the application. The error numbers recorded on the tasks showed the front end application had ended the connection: this message was consistent with a possible timeout, but it was not very helpful. The captured ILO data is shown in Table 15-2.

If you take a look at process 7, you will note that the maximum completion time does exceed 30 seconds and the variance in processing times is relatively high when compared to other processes in the application. The process spends almost no time on CPU, so this is a problem worth investigating. Where is this time going? It’s also interesting to note that this was not a process that anyone would have expected to have a performance issue. Process 3 had been the target of previous timeout investigations; it has to perform considerably more work than process 7.

Page 102: Managing SQL Performance

CHAPTER 15 ■ TESTING AND QUALITY ASSURANCE

492

Table 15-2. Timeout Errors

ILO ACTION COUNT MIN AVG MAX VAR

CPU

MIN

CPU

AVG

CPU

MAX

CPU

VAR

process 1 4 0.01 0.015 0.03 0 0 0.01 0.03 0

process 2 2 0 0 0 0 0 0 0 0

process 3 56 0.01 0.112 0.8 0.015 0.01 0.109 0.62 0.011

child 3a 36 0.04 0.078 0.15 0 0.03 0.078 0.15 0.001

child 3b 56 0 0.01 0.09 0 0 0.009 0.07 0

child 3c 36 0 0 0.01 0 0 0.001 0.01 0

child 3d 36 0 0.001 0.01 0 0 0 0.01 0

child 3e 36 0 0.001 0.01 0 0 0.001 0.01 0

child 3f 36 0 0.001 0.01 0 0 0.001 0.01 0

process 4 8 0 0.01 0.02 0 0 0.008 0.02 0

process 5 1 0.01 0.01 0.01 0 0.01 0.01 0.01 0

process 6 152 0 0.002 0.1 0 0 0.002 0.09 0

process 7 90 0 0.681 30.57 20.449 0 0.002 0.02 0

process 8 1 0 0 0 0 0.01 0.01 0.01 0

process 9 77 0 0.001 0.01 0 0 0.001 0.01 0

process 10 8 0 0.008 0.01 0 0 0.008 0.01 0

Next, let’s take a look at Table 15-3, which contains the results of a query looking for all cases in which process 7 exceeded 30 seconds.

Page 103: Managing SQL Performance

CHAPTER 15 ■ TESTING AND QUALITY ASSURANCE

493

Table 15-3. Processes Exceeding 30 Seconds

SPID ILO ACTION START TIME END TIME ELAPSED TIME ERROR

28959 process 7 22-JUL-10 05.40.00.000000 PM

22-JUL-10 09.40.31.234635 PM 30.45 –1013

29221 process 7 22-JUL-10 05.55.30.000000 PM

22-JUL-10 09.56.00.619850 PM 30.57 –1013

The start and stop times shown in Table 15-3 reflect the connection pool start and stop times, which

is a much wider window than you need in order to troubleshoot this problem. You also record internal database and CPU clock times in the ILO_ELAPSED_TIME table: it is these values that are used to calculate the elapsed times as shown in Table 15-4. Table 15-4 also shows the sequential execution of the processes, and you’ll notice that process 7 was executed repeatedly within intervals of just a few seconds.

Table 15-4. Sequential Listing of Processes with Internal Clock Times

SPID ILO ACTION GO TIME STOP TIME ELAPSED TIME CPU TIME ERROR

29221 process 7 498854690 498854690 0 0 0

28959 process 7 498856045 498859090 30.45 0 -1013

29047 process 7 498862109 498862109 0 0 0

29309 process 3 498862111 498862121 0.1 0.11 0

29309 child 3a 498862113 498862121 0.08 0.07 0

29309 child 3b 498862113 498862113 0 0 0

29309 child 3c 498862121 498862121 0 0 0

29309 child 3d 498862121 498862121 0 0 0

29309 child 3e 498862121 498862121 0 0 0

29309 child 3f 498862121 498862121 0 0 0

28959 process 7 498947571 498947571 0 0 0

29221 process 7 498948957 498952014 30.57 0 -1013

continued

Page 104: Managing SQL Performance

CHAPTER 15 ■ TESTING AND QUALITY ASSURANCE

494

Table 15-4. Sequential Listing of Processes with Internal Clock Times

SPID ILO ACTION GO TIME STOP TIME ELAPSED TIME CPU TIME ERROR

29047 process 7 498957717 498957717 0 0 0

29309 process 3 498957718 498957728 0.1 0.1 0

29309 assign_child1 498957720 498957728 0.08 0.07 0

29309 assign_child2 498957720 498957720 0 0 0

29309 assign_child3 498957728 498957728 0 0 0

29309 assign_child4 498957728 498957728 0 0.01 0

29309 assign_child5 498957728 498957728 0 0 0

29309 assign_child6 498957728 498957728 0 0 0

Looking at the two processes that exceeded thirty seconds, you have a very small timeframe when

both errors occurred. Your next step will be to check the Active Workload Repository (AWR) for that particular timeframe. Reviewing the AWR data shown in Listing 15-12, the problem is immediately clear.

Listing 15-12. AWR Output for One Hour Timeframe

Top 5 Timed Foreground Events ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Avg wait % DB Event Waits Time(s) (ms) time Wait Class ------------------------------ ------------ ----------- ------ ------ ---------- enq: TX - row lock contention 2 61 30511 78.6 Application DB CPU 12 15.0 SQL*Net break/reset to client 21,382 5 0 6.6 Application log file sync 32 0 1 .1 Commit SQL*Net message to client 10,836 0 0 .0 Network

Between the series of events shown in Table 15-2 and the AWR output shown in Listing 15-20, the cause of the timeouts becomes clear. Process 7 had been called two or even three times, when only one execution should be necessary. If those calls came in fast enough, the second process would attempt to update the same row, creating a lock and preventing the first process from committing. When process 1 could not commit in 30 seconds, the process would terminate and the second (or third) process would be able to save its changes successfully. Since the application has a built-in timeout, this problem is a minor one, and a self correcting one at that.

The tables above show data from a newly installed server with only a few executions. I’ve selected this particular data set as it is easy to use as an example, but it does make it appear as if it would have been possible to spot this problem with almost any other troubleshooting tool. However, consider this:

Page 105: Managing SQL Performance

CHAPTER 15 ■ TESTING AND QUALITY ASSURANCE

495

when this same data is reviewed on more active test servers over longer periods of time, timeouts for this process may occur on one day in any given month, and there are likely to be no more the four to six processes that exceed 30 seconds on that day. This process may execute hundreds of thousands of times over two or three months on a busy test server. And then there are test results like those shown in Table 15-1. In that case, the process is executed millions of times without a single timeout. Trying to spot this problem from an AWR report and then identifying the process that caused the application lock would take a bit more time with that many executions. And while this problem is not significant right now, it has the potential to cause the application to miss required performance targets. Due to the data recorded by the instrumentation, the problem can be monitored and addressed before that happens.

Although this is a simple example, identifying these kinds of problems can be difficult, especially during typical development test cycles. Early in unit testing, tests are not normally executed in rapid succession, so some problems may not appear until later. And once testing has moved on to load testing, an occasionally longer running process or two may not be noticed among millions of executions. Yet by using the ILO execution times to abbreviate the amount of AWR performance data that must be reviewed, problems like this can be identified and diagnosed in just a few moments. And while access to AWR and ASH data may not be available to you in all development environments, the instrumentation data you create will be.

Summary I’ve covered a wide range of information in this chapter, including execution plans and instrumentation, performance and failures, testing theory and practical application. Each of these topics could have been a chapter or even an entire book on their own, which is why there are already many, many books out there.

What I hope you will take away from this chapter is the recognition that each system has its own strengths and limitations, so any testing and measurement approach should be customized to some extent for the specific system needs and performance requirements. No single testing method can be perfectly effective for all systems, but the basic approach is fairly straightforward. Break the work down into measurable test modules, measure, adjust. and measure again. Whenever possible, minimize the changes between test iterations but keep the test realistic. You can test the functionality of your code with unit tests on a subset of the data, but testing performance requires a comparable amount of data on a comparably configured system. Verifying that a report runs exceptionally fast on a development server with little data and no other users doesn’t prove anything if that report will be run on a multiuser data warehouse. Understanding what you need to measure and confirm is crucial to preparing an appropriate test plan. And be sure to consider testing and performance early in the process. That does not necessarily mean that you need to write a perfectly optimized piece of code right out of the gate, but you should be aware of the limitations your code is likely to face in production and write the code accordingly. It also doesn’t hurt to have a few alternatives in your back pocket, so you are prepared to optimize the code and measure it once again.

Page 106: Managing SQL Performance
Page 107: Managing SQL Performance

! ! ! ! !! !

! ! ! ! !

Page 108: Managing SQL Performance

! ! !

!

! ! ! ! ! ! ! ! ! ! !

!