You are on page 1of 23

2/27/2015

UseEXPLAINPLANandTKPROFToTuneYourApplications

UseEXPLAINPLANandTKPROFToTuneYour
Applications
by Roger Schrag
Database Specialists, Inc.
About Database Specialists, Inc.
Database Specialists, Inc. provides remote DBA services and onsite database support for your mission
critical Oracle systems. Since 1995, we have been providing Oracle database consulting in Solaris, HPUX,
Linux, AIX, and Windows environments. We are DBAs, speakers, educators, and authors. Our team is
continually recognized by Oracle, at national conferences and by leading trade publications. Learn more
about our remote DBA, database tuning, andconsulting services. Or, call us at 4153440500 or 888648
0500.

Introduction
InthispaperwelldiscussanoverviewoftheEXPLAINPLANandTKPROFfunctionsbuiltintotheOracle8iserver
andlearnhowdevelopersandDBAsusethesetoolstogetthebestperformanceoutoftheirapplications.Welllookat
howtoinvokethesetoolsbothfromthecommandlineandfromgraphicaldevelopmenttools.Intheremainderofthe
paperwelldiscusshowtoreadandinterpretOracle8iexecutionplansandTKPROFreports.Welllookatlotsof
examplessothatyoullcomeawaywithasmuchpracticalknowledgeaspossible.

AnOverviewofEXPLAINPLANandTKPROF
InthissectionwelltakeahighlevellookattheEXPLAINPLANandTKPROFfacilities:whattheyare,prerequisites
forusingthem,andhowtoinvokethem.Wewillalsolookathowthesefacilitieshelpyoutuneyourapplications.

ExecutionPlansandtheEXPLAINPLANStatement
BeforethedatabaseservercanexecuteaSQLstatement,Oraclemustfirstparsethestatementanddevelopan
executionplan.TheexecutionplanisatasklistofsortsthatdecomposesapotentiallycomplexSQLoperationintoa
seriesofbasicdataaccessoperations.Forexample,aqueryagainstthedepttablemighthaveanexecutionplanthat
consistsofanindexlookuponthedeptnoindex,followedbyatableaccessbyROWID.
TheEXPLAINPLANstatementallowsyoutosubmitaSQLstatementtoOracleandhavethedatabasepreparethe
executionplanforthestatementwithoutactuallyexecutingit.Theexecutionplanismadeavailabletoyouintheformof
rowsinsertedintoaspecialtablecalledaplantable.YoumayquerytherowsintheplantableusingordinarySELECT
statementsinordertoseethestepsoftheexecutionplanforthestatementyouexplained.Youmaykeepmultiple
executionplansintheplantablebyassigningeachauniquestatement_id.Oryoumaychoosetodeletetherowsfromthe
plantableafteryouarefinishedlookingattheexecutionplan.YoucanalsorollbackanEXPLAINPLANstatementin
ordertoremovetheexecutionplanfromtheplantable.
TheEXPLAINPLANstatementrunsveryquickly,evenifthestatementbeingexplainedisaquerythatmightrunfor
hours.Thisisbecausethestatementissimplyparsedanditsexecutionplansavedintotheplantable.Theactualstatement
isneverexecutedbyEXPLAINPLAN.Alongthesesamelines,ifthestatementbeingexplainedincludesbindvariables,
thevariablesneverneedtoactuallybebound.Thevaluesthatwouldbeboundarenotrelevantsincethestatementisnot
actuallyexecuted.
http://www.dbspecialists.com/files/presentations/use_explain.html

1/23

2/27/2015

UseEXPLAINPLANandTKPROFToTuneYourApplications

YoudontneedanyspecialsystemprivilegesinordertousetheEXPLAINPLANstatement.However,youdoneedto
haveINSERTprivilegesontheplantable,andyoumusthavesufficientprivilegestoexecutethestatementyouaretrying
toexplain.Theonedifferenceisthatinordertoexplainastatementthatinvolvesviews,youmusthaveprivilegesonallof
thetablesthatmakeuptheview.Ifyoudont,youllgetanORA01039:insufficientprivilegesonunderlyingobjectsof
theviewerror.
Thecolumnsthatmakeuptheplantableareasfollows:

NameNull?Type

STATEMENT_IDVARCHAR2(30)
TIMESTAMPDATE
REMARKSVARCHAR2(80)
OPERATIONVARCHAR2(30)
OPTIONSVARCHAR2(30)
OBJECT_NODEVARCHAR2(128)
OBJECT_OWNERVARCHAR2(30)
OBJECT_NAMEVARCHAR2(30)
OBJECT_INSTANCENUMBER(38)
OBJECT_TYPEVARCHAR2(30)
OPTIMIZERVARCHAR2(255)
SEARCH_COLUMNSNUMBER
IDNUMBER(38)
PARENT_IDNUMBER(38)
POSITIONNUMBER(38)
COSTNUMBER(38)
CARDINALITYNUMBER(38)
BYTESNUMBER(38)
OTHER_TAGVARCHAR2(255)
PARTITION_STARTVARCHAR2(255)
PARTITION_STOPVARCHAR2(255)
PARTITION_IDNUMBER(38)
OTHERLONG
DISTRIBUTIONVARCHAR2(30)

ThereareotherwaystoviewexecutionplansbesidesissuingtheEXPLAINPLANstatementandqueryingtheplantable.
SQL*Pluscanautomaticallydisplayanexecutionplanaftereachstatementisexecuted.Also,therearemanyGUItools
availablethatallowyoutoclickonaSQLstatementinthesharedpoolandviewitsexecutionplan.Inaddition,TKPROF
canoptionallyincludeexecutionplansinitsreportsaswell.

TraceFilesandtheTKPROFUtility
TKPROFisautilitythatyouinvokeattheoperatingsystemlevelinordertoanalyzeSQLtracefilesandgeneratereports
thatpresentthetraceinformationinareadableform.AlthoughthedetailsofhowyouinvokeTKPROFvaryfromone
platformtothenext,OracleCorporationprovidesTKPROFwithallreleasesofthedatabaseandthebasicfunctionalityis
thesameonallplatforms.
Thetermtracefilemaybeabitconfusing.MorerecentreleasesofthedatabaseofferaproductcalledOracleTrace
CollectionServices.Also,Net8iscapableofgeneratingtracefiles.SQLtracefilesareentirelydifferent.SQLtraceisa
facilitythatyouenableordisableforindividualdatabasesessionsorfortheentireinstanceasawhole.WhenSQLtraceis
enabledforadatabasesession,theOracleserverprocesshandlingthatsessionwritesdetailedinformationaboutall
databasecallsandoperationstoatracefile.SpecialdatabaseeventsmaybesetinordertocauseOracletowriteeven
morespecificinformationsuchasthevaluesofbindvariablesintothetracefile.
http://www.dbspecialists.com/files/presentations/use_explain.html

2/23

2/27/2015

UseEXPLAINPLANandTKPROFToTuneYourApplications

SQLtracefilesaretextfilesthat,strictlyspeaking,arehumanreadable.However,theyareextremelyverbose,repetitive,
andcryptic.Forexample,ifanapplicationopensacursorandfetches1000rowsfromthecursoronerowatatime,there
willbeover1000separateentriesinthetracefile.
TKPROFisaprogramthatyouinvokeattheoperatingsystemcommandpromptinordertoreformatthetracefileintoa
formatthatismucheasiertocomprehend.EachSQLstatementisdisplayedinthereport,alongwithcountsofhowmany
timesitwasparsed,executed,andfetched.CPUtime,elapsedtime,logicalreads,physicalreads,androwsprocessed
arealsoreported,alongwithinformationaboutrecursionlevelandmissesinthelibrarycache.TKPROFcanalso
optionallyincludetheexecutionplanforeachSQLstatementinthereport,alongwithcountsofhowmanyrowswere
processedateachstepoftheexecutionplan.
TheSQLstatementscanbelistedinaTKPROFreportintheorderofhowmuchresourcetheyused,ifdesired.Also,
recursiveSQLstatementsissuedbytheSYSusertomanagethedatadictionarycanbeincludedorexcluded,and
TKPROFcanwriteSQLstatementsfromthetracedsessionintoaspoolfile.

HowEXPLAINPLANandTKPROFAidintheApplicationTuningProcess
EXPLAINPLANandTKPROFarevaluabletoolsinthetuningprocess.Tuningattheapplicationleveltypicallyyields
themostdramaticresults,andthesetwotoolscanhelpwiththetuninginmanydifferentways.
EXPLAINPLANandTKPROFallowyoutoproactivelytuneanapplicationwhileitisindevelopment.Itisrelatively
easytoenableSQLtrace,runanapplicationinatestenvironment,runTKPROFonthetracefile,andreviewtheoutput
todetermineifapplicationorschemachangesarecalledfor.EXPLAINPLANishandyforevaluatingindividualSQL
statements.
Byreviewingexecutionplans,youcanalsovalidatethescalabilityofanapplication.Ifthedatabaseoperationsare
dependentuponfulltablescansoftablesthatcouldgrowquitelarge,thentheremaybescalabilityproblemsahead.On
theotherhand,iflargetablesareaccessedviaselectiveindexes,thenscalabilitymaynotbeaproblem.
EXPLAINPLANandTKPROFmayalsobeusedinanexistingproductionenvironmentinordertozeroinonresource
intensiveoperationsandgetinsightsintohowthecodemaybeoptimized.TKPROFcanfurtherbeusedtoquantifythe
resourcesrequiredbyspecificdatabaseoperationsorapplicationfunctions.
EXPLAINPLANisalsohandyforestimatingresourcerequirementsinadvance.Supposeyouhaveanadhocreporting
requestagainstaverylargedatabase.RunningqueriesthroughEXPLAINPLANwillletyoudetermineinadvanceifthe
queriesarefeasibleoriftheywillberesourceintensiveandwilltakeunacceptablylongtorun.

GeneratingExecutionPlansandTKPROFReports
Inthissectionwewilldiscussthedetailsofhowtogenerateexecutionplans(bothwiththeEXPLAINPLANstatement
andothermethods)andhowtogenerateSQLtracefilesandcreateTKPROFreports.

UsingtheEXPLAINPLANStatement
BeforeyoucanusetheEXPLAINPLANstatement,youmusthaveINSERTprivilegesonaplantable.Theplantable
canhaveanynameyoulike,butthenamesanddatatypesofthecolumnsarenotflexible.Youwillfindascriptcalled
utlxplan.sqlin$ORACLE_HOME/rdbms/adminthatcreatesaplantablewiththenameplan_tableinthelocalschema.If
youusethisscripttocreateyourplantable,youcanbeassuredthatthetablewillhavetherightdefinitionforusewith
EXPLAINPLAN.
Onceyouhaveaccesstoaplantable,youarereadytoruntheEXPLAINPLANstatement.Thesyntaxisasfollows:
http://www.dbspecialists.com/files/presentations/use_explain.html

3/23

2/27/2015

UseEXPLAINPLANandTKPROFToTuneYourApplications

EXPLAINPLAN[SETSTATEMENT_ID=<stringinsinglequotes>]
[INTO<plantablename>]
FOR<SQLstatement>;

IfyoudonotspecifytheINTOclause,thenOracleassumesthenameoftheplantableisplan_table.Youcanusethe
SETclausetoassignanametotheexecutionplan.Thisisusefulifyouwanttobeabletohavemultipleexecutionplans
storedintheplantableatoncegivingeachexecutionplanadistinctnameenablesyoutodeterminewhichrowsinthe
plantablebelongtowhichexecutionplan.
TheEXPLAINPLANstatementrunsquicklybecauseallOraclehastodoisparsetheSQLstatementbeingexplained
andstoretheexecutionplanintheplantable.TheSQLstatementcanincludebindvariables,althoughthevariableswill
notgetboundandthevaluesofthebindvariableswillbeirrelevant.
IfyouissuetheEXPLAINPLANstatementfromSQL*Plus,youwillgetbackthefeedbackmessageExplained.At
thispointtheexecutionplanfortheexplainedSQLstatementhasbeeninsertedintotheplantable,andyoucannow
querytheplantabletoexaminetheexecutionplan.
Executionplansareahierarchicalarrangementofsimpledataaccessoperations.Becauseofthehierarchy,youneedto
useaCONNECTBYclauseinyourqueryfromtheplantable.UsingtheLPADfunction,youcancausetheoutputtobe
formattedinsuchawaythattheindentinghelpsyoutraversethehierarchy.Therearemanydifferentwaystoformatthe
dataretrievedfromtheplantable.Noonequeryisthebest,becausetheplantableholdsalotofdetailedinformation.
DifferentDBAswillfinddifferentaspectsmoreusefulindifferentsituations.
AsimpleSQL*Plusscripttoretrieveanexecutionplanfromtheplantableisasfollows:

REM
REMexplain.sql
REM
SETVERIFYOFF
SETPAGESIZE100
ACCEPTstmt_idCHARPROMPT"Enterstatement_id:"
COLidFORMAT999
COLparent_idFORMAT999HEADING"PARENT"
COLoperationFORMATa35TRUNCATE
COLobject_nameFORMATa30
SELECTid,parent_id,LPAD('',LEVEL1)||operation||''||
optionsoperation,object_name
FROMplan_table
WHEREstatement_id='&stmt_id'
STARTWITHid=0
ANDstatement_id='&stmt_id'
CONNECTBYPRIOR
id=parent_id
ANDstatement_id='&stmt_id';

Ihaveasimplequerythatwewilluseinafewexamples.Wellcallthistheinvoiceitemquery.Thequeryisasfollows:

http://www.dbspecialists.com/files/presentations/use_explain.html

4/23

2/27/2015

UseEXPLAINPLANandTKPROFToTuneYourApplications

SELECTa.customer_name,a.customer_number,b.invoice_number,
b.invoice_type,b.invoice_date,b.total_amount,c.line_number,
c.part_number,c.quantity,c.unit_cost
FROMcustomersa,invoicesb,invoice_itemsc
WHEREc.invoice_id=:b1
ANDc.line_number=:b2
ANDb.invoice_id=c.invoice_id
ANDa.customer_id=b.customer_id;

Theexplain.sqlSQL*Plusscriptabovedisplaystheexecutionplanfortheinvoiceitemqueryasfollows:

IDPARENTOPERATIONOBJECT_NAME

0SELECTSTATEMENT
10NESTEDLOOPS
21NESTEDLOOPS
32TABLEACCESSBYINDEXROWIDINVOICE_ITEMS
43INDEXUNIQUESCANINVOICE_ITEMS_PK
52TABLEACCESSBYINDEXROWIDINVOICES
65INDEXUNIQUESCANINVOICES_PK
71TABLEACCESSBYINDEXROWIDCUSTOMERS
87INDEXUNIQUESCANCUSTOMERS_PK

TheexecutionplanshowsthatOracleisusingnestedloopsjoinstojointhreetables,andthataccessesfromallthree
tablesarebyuniqueindexlookup.Thisisprobablyaveryefficientquery.Wewilllookathowtoreadexecutionplansin
greaterdetailinalatersection.
Theexplain.sqlscriptfordisplayinganexecutionplanisverybasicinthatitdoesnotdisplayalotoftheinformation
containedintheplantable.Thingsleftoffofthedisplayincludeoptimizerestimatedcost,cardinality,partitioninformation
(onlyrelevantwhenaccessingpartitionedtables),andparallelisminformation(onlyrelevantwhenexecutingparallel
queriesorparallelDML).
IfyouareusingOracle8.1.5orlater,youcanfindtwoplanqueryscriptsin$ORACLE_HOME/rdbms/admin.utlxpls.sql
isintendedfordisplayingexecutionplansofstatementsthatdonotinvolveparallelprocessing,whileutlxplp.sqlshows
additionalinformationpertainingtoparallelprocessing.Theoutputofthelatterscriptismoreconfusing,soonlyuseit
whenparallelqueryorDMLcomeintoplay.Theoutputfromutlxpls.sqlfortheinvoiceitemqueryisasfollows:

PlanTable

|Operation|Name|Rows|Bytes|Cost|Pstart|Pstop|

|SELECTSTATEMENT||1|39|4|||
|NESTEDLOOPS||1|39|4|||
|NESTEDLOOPS||1|27|3|||
|TABLEACCESSBYINDEXR|INVOICE_I|1|15|2|||
|INDEXUNIQUESCAN|INVOICE_I|2||1|||
|TABLEACCESSBYINDEXR|INVOICES|2|24|1|||
|INDEXUNIQUESCAN|INVOICES_|2|||||
|TABLEACCESSBYINDEXRO|CUSTOMERS|100|1K|1|||
|INDEXUNIQUESCAN|CUSTOMERS|100|||||

http://www.dbspecialists.com/files/presentations/use_explain.html

5/23

2/27/2015

UseEXPLAINPLANandTKPROFToTuneYourApplications

Whenyounolongerneedanexecutionplan,youshoulddeleteitfromtheplantable.Youcandothisbyrollingbackthe
EXPLAINPLANstatement(ifyouhavenotcommittedyet)orbydeletingrowsfromtheplantable.Ifyouhavemultiple
executionplansintheplantable,thenyoushoulddeleteselectivelybystatement_id.NotethatifyouexplaintwoSQL
statementsandassignboththesamestatement_id,youwillgetanuglycartesianproductwhenyouquerytheplantable!

TheAutotraceFeatureofSQL*Plus
SQL*Plushasanautotracefeaturewhichallowsyoutoautomaticallydisplayexecutionplansandhelpfulstatisticsfor
eachstatementexecutedinaSQL*PlussessionwithouthavingtousetheEXPLAINPLANstatementorquerytheplan
table.YouturnthisfeatureonandoffwiththefollowingSQL*Pluscommand:

SETAUTOTRACEOFF|ON|TRACEONLY[EXPLAIN][STATISTICS]

WhenyouturnonautotraceinSQL*Plus,thedefaultbehaviorisforSQL*Plustoexecuteeachstatementanddisplaythe
resultsinthenormalfashion,followedbyanexecutionplanlistingandalistingofvariousserversideresourcesusedto
executethestatement.ByusingtheTRACEONLYkeyword,youcanhaveSQL*Plussuppressthequeryresults.By
usingtheEXPLAINorSTATISTICSkeywords,youcanhaveSQL*Plusdisplayjusttheexecutionplanwithoutthe
resourcestatisticsorjustthestatisticswithouttheexecutionplan.
InordertohaveSQL*Plusdisplayexecutionplans,youmusthaveprivilegesonaplantablebythenameofplan_table.In
ordertohaveSQL*Plusdisplaytheresourcestatistics,youmusthaveSELECTprivilegesonv$sesstat,v$statname,and
v$session.Thereisascriptin$ORACLE_HOME/sqlplus/admincalledplustrce.sqlwhichcreatesarolewiththesethree
privilegesinit,butthisscriptisnotrunautomaticallybytheOracleinstaller.
TheautotracefeatureofSQL*Plusmakesitextremelyeasytogenerateandviewexecutionplans,withresourcestatistics
asanaddedbonus.Onekeydrawback,however,isthatthestatementbeingexplainedmustactuallybeexecutedbythe
databaseserverbeforeSQL*Pluswilldisplaytheexecutionplan.Thismakesthetoolunusableinthesituationwhereyou
wouldliketopredicthowlonganoperationmighttaketocomplete.
AsampleoutputfromSQL*Plusfortheinvoiceitemqueryisasfollows:

ExecutionPlan

0SELECTSTATEMENTOptimizer=CHOOSE(Cost=4Card=1Bytes=39)
10NESTEDLOOPS(Cost=4Card=1Bytes=39)
21NESTEDLOOPS(Cost=3Card=1Bytes=27)
32TABLEACCESS(BYINDEXROWID)OF'INVOICE_ITEMS'(Cost
=2Card=1Bytes=15)
43INDEX(UNIQUESCAN)OF'INVOICE_ITEMS_PK'(UNIQUE)(
Cost=1Card=2)
52TABLEACCESS(BYINDEXROWID)OF'INVOICES'(Cost=1Ca
rd=2Bytes=24)
65INDEX(UNIQUESCAN)OF'INVOICES_PK'(UNIQUE)
71TABLEACCESS(BYINDEXROWID)OF'CUSTOMERS'(Cost=1Car
d=100Bytes=1200)
87INDEX(UNIQUESCAN)OF'CUSTOMERS_PK'(UNIQUE)

http://www.dbspecialists.com/files/presentations/use_explain.html

6/23

2/27/2015

UseEXPLAINPLANandTKPROFToTuneYourApplications

Statistics

0recursivecalls
0dbblockgets
8consistentgets
0physicalreads
0redosize
517bytessentviaSQL*Nettoclient
424bytesreceivedviaSQL*Netfromclient
2SQL*Netroundtripsto/fromclient
0sorts(memory)
0sorts(disk)
1rowsprocessed

Althoughwehaventdiscussedhowtoreadanexecutionplanyet,youcanseethattheoutputfromSQL*Plusprovides
thesamebasicinformation,withseveraladditionaldetailsintheformofestimatesfromthequeryoptimizer.

UsingGUIToolstoViewExecutionPlans
TherearemanyGUItoolsavailablethatallowyoutoviewexecutionplansforSQLstatementsyouspecifyorfor
statementsalreadysittinginthesharedpoolofthedatabaseinstance.Anycomprehensivedatabasemanagementtoolwill
offerthiscapability,butthereareseveralfreetoolsavailablefordownloadontheinternetthathavethisfeatureaswell.
OnetoolinparticularthatIreallylikeisTOAD(theToolforOracleApplicationDevelopers).AlthoughTOADwas
originallydevelopedasafreetool,QuestSoftwarenowownsTOADanditisavailableinbothafreeversion(limited
functionality)andanenhancedversionthatmaybepurchased(fullfeatureset).YoumaydownloadTOADfromQuest
Softwareathttp://www.toadsoft.com/downld.html.TOADhaslotsofhandyfeatures.Theonerelevanttoushereisthe
abilitytoclickonanySQLstatementinthesharedpoolandinstantlyviewitsexecutionplan.
AswiththeEXPLAINPLANstatementandtheautotracefacilityinSQL*Plus,youwillneedtohaveaccesstoaplan
table.HereisTOADsrenditionoftheexecutionplanfortheinvoiceitemquerywevebeenusing:

http://www.dbspecialists.com/files/presentations/use_explain.html

7/23

2/27/2015

UseEXPLAINPLANandTKPROFToTuneYourApplications

YoucanseethattheinformationdisplayedisalmostidenticaltothatfromtheautotracefacilityinSQL*Plus.Onenice
featureofTOADsexecutionplanvieweristhatyoucancollapseandexpandtheindividualoperationsthatmakeupthe
executionplan.Also,theverticalandhorizontallinesconnectingdifferentstepshelpyoukeeptrackofthenestingand
whichchildoperationsgowithwhichparentoperationsinthehierarchy.Thebenefitsofthesefeaturesbecomemore
apparentwhenworkingwithextremelycomplicatedexecutionplans.
Unfortunately,whenlookingatexecutionplansforSQLstatementsthatinvolvedatabaselinksorparallelism,TOAD
leavesoutcriticalinformationthatispresentintheplantableandisreportedbytheautotracefeatureofSQL*Plus.
PerhapsthisdeficiencyonlyexistsinthefreeversionofTOADIwouldliketothinkthatifyoupayforthefullversionof
TOAD,youllgetcompleteexecutionplans.

GeneratingaSQLTraceFile
SQLtracemaybeenabledattheinstanceorsessionlevel.ToenableSQLtraceattheinstancelevel,addthefollowing
parametersettingtotheinstanceparameterfileandrestartthedatabaseinstance:

sql_trace=true

WhenanOracleinstancestartsupwiththeaboveparametersetting,everydatabasesessionwillruninSQLtracemode,
meaningthatallSQLoperationsforeverydatabasesessionwillbewrittentotracefiles.Eventhedaemonprocesseslike
PMONandSMONwillbetraced!Inpractice,enablingSQLtraceattheinstancelevelisusuallynotveryuseful.Itcan
beoverpowering,sortoflikeusingafirehosetopouryourselfaglassofwater.
http://www.dbspecialists.com/files/presentations/use_explain.html

8/23

2/27/2015

UseEXPLAINPLANandTKPROFToTuneYourApplications

ItismoretypicaltoenableSQLtraceinaspecificsession.YoucanturnSQLtraceonandoffasdesiredinorderto
tracejusttheoperationsthatyouwishtotrace.Ifyouhaveaccesstothedatabasesessionyouwishtotrace,thenusethe
ALTERSESSIONstatementasfollowstoenableanddisableSQLtrace:

ALTERSESSIONSETsql_trace=TRUE|FALSE;

ThistechniqueworkswellifyouhaveaccesstotheapplicationsourcecodeandcanaddinALTERSESSION
statementsatwill.ItalsoworkswellwhentheapplicationrunsfromSQL*PlusandyoucanexecuteALTERSESSION
statementsattheSQL*Pluspromptbeforeinvokingtheapplication.
InsituationswhereyoucannotinvokeanALTERSESSIONcommandfromthesessionyouwishtotraceaswith
prepackagedapplications,forexampleyoucanconnecttothedatabaseasaDBAuserandinvokethedbms_system
builtinpackageinordertoturnonoroffSQLtraceinanothersession.Youdothisbyqueryingv$sessiontofindtheSID
andserialnumberofthesessionyouwishtotraceandtheninvokingthedbms_systempackagewithacommandofthe
form:

EXECUTESYS.dbms_system.set_sql_trace_in_session(<SID>,<serial#>,TRUE|FALSE);

WhenyouenableSQLtraceinasessionforthefirsttime,theOracleserverprocesshandlingthatsessionwillcreatea
tracefileinthedirectoryonthedatabaseserverdesignatedbytheuser_dump_destinitializationparameter.Astheserver
iscalledbytheapplicationtoperformdatabaseoperations,theserverprocesswillappendtothetracefile.
Notethattracingadatabasesessionthatisusingmultithreadedserver(MTS)isabitcomplicatedbecauseeachdatabase
requestfromtheapplicationcouldgetpickedupbyadifferentserverprocess.Inthissituation,eachserverprocesswill
createatracefilecontainingtraceinformationabouttheoperationsperformedbythatprocessonly.Thismeansthatyou
willpotentiallyhavetocombinemultipletracefilestogethertogetthefullpictureofhowtheapplicationinteractedwiththe
database.Furthermore,ifmultiplesessionsarebeingtracedatonce,itwillbehardtotellwhichoperationsinthetracefile
belongtowhichsession.Forthesereasons,youshouldusededicatedservermodewhentracingadatabasesessionwith
SQLtrace.
SQLtracefilescontaindetailedtiminginformation.Bydefault,Oracledoesnottracktiming,soalltimingfiguresintrace
fileswillshowaszero.Ifyouwouldliketoseelegitimatetiminginformation,thenyouneedtoenabletimedstatistics.You
candothisattheinstancelevelbysettingthefollowingparameterintheinstanceparameterfileandrestartingtheinstance:

timed_statistics=true

Youcanalsodynamicallyenableordisabletimedstatisticscollectionateithertheinstanceorthesessionlevelwiththe
followingcommands:

ALTERSYSTEMSETtimed_statistics=TRUE|FALSE;
ALTERSESSIONSETtimed_statistics=TRUE|FALSE;

http://www.dbspecialists.com/files/presentations/use_explain.html

9/23

2/27/2015

UseEXPLAINPLANandTKPROFToTuneYourApplications

Thereisnoknownwaytoenabletimedstatisticscollectionforanindividualsessionfromanothersession(akintothe
SYS.dbms_system.set_sql_trace_in_sessionbuiltin).
ThereisveryhighoverheadassociatedwithenablingSQLtrace.SomeDBAsbelievetheperformancepenaltycouldbe
over25%.AnotherconcernisthatenablingSQLtracecausesthegenerationofpotentiallylargetracefiles.Forthese
reasons,youshoulduseSQLtracesparingly.Onlytracewhatyouneedtotraceandthinkverycarefullybeforeenabling
SQLtraceattheinstancelevel.
Ontheotherhand,thereislittle,ifany,measurableperformancepenaltyinenablingtimedstatisticscollection.Many
DBAsrunproductiondatabaseswithtimedstatisticscollectionenabledatthesystemlevelsothatvarioussystemstatistics
(morethanjustSQLtracefiles)willincludedetailedtiminginformation.NotethatOracle8.1.5hadsomeseriousmemory
corruptionbugsassociatedwithenablingtimedstatisticscollectionattheinstancelevel,buttheseseemtohavebeenfixed
inOracle8.1.6.
OnUnixplatforms,OraclewilltypicallysetpermissionssothatonlytheoracleuserandmembersofthedbaUnixgroup
canreadthetracefiles.IfyouwantanybodywithaUnixlogintobeabletoreadthetracefiles,thenyoushouldsetthe
followingundocumented(butsupported)initializationparameterintheparameterfile:

_trace_files_public=true

Ifyoutraceadatabasesessionthatmakesalargenumberofcallstothedatabaseserver,thetracefilecangetquitelarge.
Theinitializationparametermax_dump_file_sizeallowsyoutosetamaximumtracefilesize.OnUnixplatforms,this
parameterisspecifiedinunitsof512byteblocks.Thusasettingof10240willlimittracefilesto5Mbapiece.Whena
SQLtracefilereachesthemaximumsize,thedatabaseserverprocessstopswritingtraceinformationtothetracefile.On
Unixplatformstherewillbenolimitontracefilesizeifyoudonotexplicitlysetthemax_dump_file_sizeparameter.
Ifyouaretracingasessionandrealizethatthetracefileisabouttoreachthelimitsetbymax_dump_file_size,youcan
eliminatethelimitdynamicallysothatyoudontlosetraceinformation.Todothis,querythePIDcolumninv$processto
findtheOraclePIDoftheprocesswritingthetracefile.ThenexecutethefollowingstatementsinSQL*Plus:

CONNECT/ASSYSDBA
ORADEBUGSETORAPID<pid>
ORADEBUGUNLIMIT

RunningTKPROFonaSQLTraceFile
BeforeyoucanuseTKPROF,youneedtogenerateatracefileandlocateit.Oraclewritestracefilesonthedatabase
servertothedirectoryspecifiedbytheuser_dump_destinitializationparameter.(DaemonprocessessuchasPMON
writetheirtracefilestothedirectoryspecifiedbybackground_dump_dest.)OnUnixplatforms,thetracefilewillhavea
namethatincorporatestheoperatingsystemPIDoftheserverprocesswritingthetracefile.
Iftherearealotoftracefilesintheuser_dump_destdirectory,itcouldbetrickytofindtheoneyouwant.Onetacticisto
examinethetimestampsonthefiles.AnothertechniqueistoembedacommentinaSQLstatementintheapplicationthat
willmakeitswayintothetracefile.Anexampleofthisisasfollows:

ALTERSESSION/*Moduleglpost.c*/SETsql_trace=TRUE;
http://www.dbspecialists.com/files/presentations/use_explain.html

10/23

2/27/2015

UseEXPLAINPLANandTKPROFToTuneYourApplications

BecauseTKPROFisautilityyouinvokefromtheoperatingsystemandnotfromwithinadatabasesession,therewill
naturallybesomevariationintheuserinterfacefromoneoperatingsystemplatformtoanother.OnUnixplatforms,you
runTKPROFfromtheoperatingsystempromptwithasyntaxasfollows:

tkprof<tracefile><outputfile>[explain=<username/password>][sys=n]\
[insert=<filename>][record=<filename>][sort=<keyword>]

IfyouinvokeTKPROFwithnoargumentsatall,youwillgetahelpscreenlistingalloftheoptions.Thisisespecially
helpfulbecauseTKPROFoffersmanysortcapabilities,butyouselectthedesiredsortbyspecifyingacryptickeyword.
Thehelpscreenidentifiesallofthesortkeywords.
Initssimplestform,yourunTKPROFspecifyingthenameofaSQLtracefileandanoutputfilename.TKPROFwillread
thetracefileandgenerateareportfilewiththeoutputfilenameyouspecified.TKPROFwillnotconnecttothedatabase,
andthereportwillnotincludeexecutionplansfortheSQLstatements.SQLstatementsthatwereexecutedbytheSYS
userrecursively(todynamicallyallocateanextentinadictionarymanagedtablespace,forexample)willbeincludedinthe
report,andthestatementswillappearinthereportapproximatelyintheorderinwhichtheywereexecutedinthe
databasesessionthatwastraced.
Ifyouincludetheexplainkeyword,TKPROFwillconnecttothedatabaseandexecuteanEXPLAINPLANstatement
foreachSQLstatementfoundinthetracefile.Theexecutionplanresultswillbeincludedinthereportfile.Aswewillsee
later,TKPROFmergesvaluableinformationfromthetracefileintotheexecutionplandisplay,makingthisjustaboutthe
mostvaluablewaytodisplayanexecutionplan.NotethattheusernameyouspecifywhenrunningTKPROFshouldbe
thesameastheusernameconnectedinthedatabasesessionthatwastraced.Youdonotneedtohaveaplantablein
ordertousetheexplainkeywordTKPROFwillcreateanddropitsownplantableifneeded.
Ifyouspecifysys=n,TKPROFwillexcludefromthereportSQLstatementsinitiatedbyOracleastheSYSuser.Thiswill
makeyourreportlooktidierbecauseitwillonlycontainstatementsactuallyissuedbyyourapplication.Thetheoryisthat
OracleinternalSQLhasalreadybeenfullyoptimizedbythekerneldevelopersatOracleCorporation,soyoushouldnot
havetodealwithit.However,usingsys=nwillexcludepotentiallyvaluableinformationfromtheTKPROFreport.
SupposetheSGAisnotproperlysizedontheinstanceandOracleisspendingalotoftimeresolvingdictionarycache
misses.ThiswouldmanifestitselfinlotsoftimespentonrecursiveSQLstatementsinitiatedbytheSYSuser.Usingsys=n
wouldexcludethisinformationfromthereport.
Ifyouspecifytheinsertkeyword,TKPROFwillgenerateaSQLscriptinadditiontotheregularreport.ThisSQLscript
createsatablecalledtkprof_tableandinsertsonerowforeachSQLstatementdisplayedonthereport.Therowwill
containthetextoftheSQLstatementtracedandallofthestatisticsdisplayedinthereport.Youcouldusethisfeatureto
effectivelyloadtheTKPROFreportintothedatabaseanduseSQLtoanalyzeandmanipulatethestatistics.Ivenever
neededtousethisfeature,butIsupposeitcouldbehelpfulinsomesituations.
Ifyouspecifytherecordkeyword,TKPROFwillgenerateanothertypeofSQLscriptinadditiontotheregularreport.
ThisSQLscriptwillcontainacopyofeachSQLstatementissuedbytheapplicationwhiletracingwasenabled.You
couldgetthissameinformationfromtheTKPROFreportitself,butthiswaycouldsavesomecuttingandpasting.
Thesortkeywordisextremelyuseful.Typically,aTKPROFreportmayincludehundredsofSQLstatements,butyou
mayonlybeinterestedinafewresourceintensivequeries.ThesortkeywordallowsyoutoorderthelistingoftheSQL
statementssothatyoudonthavetoscantheentirefilelookingforresourcehogs.Insomeways,thesortfeatureistoo
powerfulforitsowngood.Forexample,youcannotsortstatementsbyCPUtimeconsumedinsteadyousortbyCPU
http://www.dbspecialists.com/files/presentations/use_explain.html

11/23

2/27/2015

UseEXPLAINPLANandTKPROFToTuneYourApplications

timespentparsing,CPUtimespentexecuting,orCPUtimespentfetching.
AsampleTKPROFreportfortheinvoiceitemquerywevebeenusingsofarisasfollows:

TKPROF:Release8.1.6.1.0ProductiononWedAug919:06:362000
(c)Copyright1999OracleCorporation.Allrightsreserved.
Tracefile:example.trc
Sortoptions:default
********************************************************************************
count=numberoftimesOCIprocedurewasexecuted
cpu=cputimeinsecondsexecuting
elapsed=elapsedtimeinsecondsexecuting
disk=numberofphysicalreadsofbuffersfromdisk
query=numberofbuffersgottenforconsistentread
current=numberofbuffersgottenincurrentmode(usuallyforupdate)
rows=numberofrowsprocessedbythefetchorexecutecall
********************************************************************************
ALTERSESSION/*TKPROFexample*/SETsql_trace=TRUE
callcountcpuelapseddiskquerycurrentrows

Parse00.000.000000
Execute10.000.000000
Fetch00.000.000000

total10.000.000000
Missesinlibrarycacheduringparse:0
Missesinlibrarycacheduringexecute:1
Optimizergoal:CHOOSE
Parsinguserid:34(RSCHRAG)
********************************************************************************
ALTERSESSIONSETtimed_statistics=TRUE
callcountcpuelapseddiskquerycurrentrows

Parse10.000.000000
Execute10.000.000000
Fetch00.000.000000

total20.000.000000
Missesinlibrarycacheduringparse:1
Optimizergoal:CHOOSE
Parsinguserid:34(RSCHRAG)
********************************************************************************
SELECTa.customer_name,a.customer_number,b.invoice_number,
b.invoice_type,b.invoice_date,b.total_amount,c.line_number,
c.part_number,c.quantity,c.unit_cost
FROMcustomersa,invoicesb,invoice_itemsc
WHEREc.invoice_id=:b1
ANDc.line_number=:b2
ANDb.invoice_id=c.invoice_id
ANDa.customer_id=b.customer_id
callcountcpuelapseddiskquerycurrentrows

Parse10.050.020000
http://www.dbspecialists.com/files/presentations/use_explain.html

12/23

2/27/2015

UseEXPLAINPLANandTKPROFToTuneYourApplications

Execute10.000.000000
Fetch20.000.008801

total40.050.028801
Missesinlibrarycacheduringparse:1
Optimizergoal:CHOOSE
Parsinguserid:34(RSCHRAG)
RowsRowSourceOperation

1NESTEDLOOPS
1NESTEDLOOPS
1TABLEACCESSBYINDEXROWIDINVOICE_ITEMS
1INDEXUNIQUESCAN(objectid21892)
1TABLEACCESSBYINDEXROWIDINVOICES
1INDEXUNIQUESCAN(objectid21889)
1TABLEACCESSBYINDEXROWIDCUSTOMERS
1INDEXUNIQUESCAN(objectid21887)
RowsExecutionPlan

0SELECTSTATEMENTGOAL:CHOOSE
1NESTEDLOOPS
1NESTEDLOOPS
1TABLEACCESSGOAL:ANALYZED(BYINDEXROWID)OF
'INVOICE_ITEMS'
1INDEXGOAL:ANALYZED(UNIQUESCAN)OF'INVOICE_ITEMS_PK'
(UNIQUE)
1TABLEACCESSGOAL:ANALYZED(BYINDEXROWID)OF
'INVOICES'
1INDEXGOAL:ANALYZED(UNIQUESCAN)OF'INVOICES_PK'
(UNIQUE)
1TABLEACCESSGOAL:ANALYZED(BYINDEXROWID)OF'CUSTOMERS'
1INDEXGOAL:ANALYZED(UNIQUESCAN)OF'CUSTOMERS_PK'
(UNIQUE)
********************************************************************************
ALTERSESSIONSETsql_trace=FALSE
callcountcpuelapseddiskquerycurrentrows

Parse10.000.000000
Execute10.000.000000
Fetch00.000.000000

total20.000.000000
Missesinlibrarycacheduringparse:1
Optimizergoal:CHOOSE
Parsinguserid:34(RSCHRAG)
********************************************************************************
OVERALLTOTALSFORALLNONRECURSIVESTATEMENTS
callcountcpuelapseddiskquerycurrentrows

Parse30.050.020000
Execute40.000.000000
Fetch20.000.008801

total90.050.028801
http://www.dbspecialists.com/files/presentations/use_explain.html

13/23

2/27/2015

UseEXPLAINPLANandTKPROFToTuneYourApplications

Missesinlibrarycacheduringparse:3
Missesinlibrarycacheduringexecute:1
OVERALLTOTALSFORALLRECURSIVESTATEMENTS
callcountcpuelapseddiskquerycurrentrows

Parse240.020.041010
Execute620.010.050000
Fetch1260.020.0261980100

total2120.050.1171981100
Missesinlibrarycacheduringparse:11
4userSQLstatementsinsession.
24internalSQLstatementsinsession.
28SQLstatementsinsession.
1statementEXPLAINedinthissession.
********************************************************************************
Tracefile:example.trc
Tracefilecompatibility:8.00.04
Sortoptions:default
1sessionintracefile.
4userSQLstatementsintracefile.
24internalSQLstatementsintracefile.
28SQLstatementsintracefile.
15uniqueSQLstatementsintracefile.
1SQLstatementsEXPLAINedusingschema:
RSCHRAG.prof$plan_table
Defaulttablewasused.
Tablewascreated.
Tablewasdropped.
381linesintracefile.

YoucanseethatthereisalotgoingoninaTKPROFreport.Wewilltalkabouthowtoreadthereportandinterpretthe
differentstatisticsinthenextsection.

InterpretingExecutionPlansandTKPROFReports
InthissectionwewilldiscusshowtoreadandinterpretexecutionplansandTKPROFreports.Whilegeneratingan
executionplanlistingorcreatingaTKPROFreportfileisusuallyastraightforwardprocess,analyzingthedataand
reachingthecorrectconclusionscanbemoreofanart.Welllookatlotsofexamplesalongtheway.

UnderstandingExecutionPlans
Anexecutionplanisahierarchicalstructuresomewhatlikeaninvertedtree.TheSQLstatementbeingexaminedcanbe
thoughtofastherootofthetree.Thiswillbethefirstlineonanexecutionplanlisting,thelinethatisleastindented.This
statementcanbethoughtofastheresultofoneormoresubordinateoperations.Eachofthesesubordinateoperationscan
possiblybedecomposedfurther.Thisdecompositionprocesscontinuesrepeatedlyuntileventuallyeventhemostcomplex
SQLstatementisbrokendownintoasetofbasicdataaccessoperations.
Considerthefollowingsimplequeryandexecutionplan:

http://www.dbspecialists.com/files/presentations/use_explain.html

14/23

2/27/2015

UseEXPLAINPLANandTKPROFToTuneYourApplications

SELECTcustomer_id,customer_number,customer_name
FROMcustomers
WHEREUPPER(customer_name)LIKE'ACME%'
ORDERBYcustomer_name;
IDPARENTOPERATIONOBJECT_NAME

0SELECTSTATEMENT
10SORTORDERBY
21TABLEACCESSFULLCUSTOMERS

TherootoperationthatwhichweexplainedisaSELECTstatement.Theoutputofthestatementwillbetheresultsof
asortoperation(forthepurposesofsatisfyingtheORDERBYclause).Theinputtothesortwillbetheresultsofafull
tablescanofthecustomerstable.Statedmoreclearly,thedatabaseserverwillexecutethisquerybycheckingeveryrow
inthecustomerstableforacriteriamatchandsortingtheresults.PerhapsthedeveloperexpectedOracletouseanindex
onthecustomer_namecolumntoavoidafulltablescan,buttheuseoftheUPPERfunctiondefeatedtheindex.(A
functionbasedindexcouldbedeployedtomakethisquerymoreefficient.)
Considerthefollowingqueryandexecutionplan:

SELECTa.customer_name,b.invoice_number,b.invoice_date
FROMcustomersa,invoicesb
WHEREb.invoice_date>TRUNC(SYSDATE1)
ANDa.customer_id=b.customer_id;
IDPARENTOPERATIONOBJECT_NAME

0SELECTSTATEMENT
10NESTEDLOOPS
21TABLEACCESSBYINDEXROWIDINVOICES
32INDEXRANGESCANINVOICES_DATE
41TABLEACCESSBYINDEXROWIDCUSTOMERS
54INDEXUNIQUESCANCUSTOMERS_PK

Again,therootoperationisaSELECTstatement.Thistime,theSELECTstatementgetsitsinputfromtheresultsofa
nestedloopsjoinoperation.Thenestedloopsoperationtakesasinputtheresultsofaccessestotheinvoicesand
customerstables.(Youcantellfromtheindentingthataccessestobothtablesfeeddirectlyintothenestedloops
operation.)Theinvoicestableisaccessedbyarangescanoftheinvoices_dateindex,whilethecustomerstableis
accessedbyauniquescanofthecustomers_pkindex.
Inplainerlanguage,hereishowOraclewillexecutethisquery:Oraclewillperformarangescanontheinvoices_date
indextofindtheROWIDsofallrowsintheinvoicestablethathaveaninvoicedatematchingthequerycriteria.Foreach
ROWIDfound,Oraclewillfetchthecorrespondingrowfromtheinvoicestable,lookupthecustomer_idfromthe
invoicesrecordinthecustomers_pkindex,andusetheROWIDfoundinthecustomers_pkindexentrytofetchthe
correctcustomerrecord.This,ineffect,joinstherowsfetchedfromtheinvoicestablewiththeircorrespondingmatchesin
thecustomerstable.Theresultsofthenestedloopsjoinoperationarereturnedasthequeryresults.
Considerthefollowingqueryandexecutionplan:

SELECTa.customer_name,COUNT(DISTINCTb.invoice_id)"OpenInvoices",
COUNT(c.invoice_id)"OpenInvoiceItems"
FROMcustomersa,invoicesb,invoice_itemsc
http://www.dbspecialists.com/files/presentations/use_explain.html

15/23

2/27/2015

UseEXPLAINPLANandTKPROFToTuneYourApplications

WHEREb.invoice_status='OPEN'
ANDa.customer_id=b.customer_id
ANDc.invoice_id(+)=b.invoice_id
GROUPBYa.customer_name;
IDPARENTOPERATIONOBJECT_NAME

0SELECTSTATEMENT
10SORTGROUPBY
21NESTEDLOOPSOUTER
32HASHJOIN
43TABLEACCESSBYINDEXROWIDINVOICES
54INDEXRANGESCANINVOICES_STATUS
63TABLEACCESSFULLCUSTOMERS
72INDEXRANGESCANINVOICE_ITEMS_PK

Thisexecutionplanismorecomplexthantheprevioustwo,andhereyoucanstarttogetafeelforthewayinwhich
complexoperationsgetbrokendownintosimplersubordinateoperations.Toexecutethisquery,thedatabaseserverwill
dothefollowing:FirstOraclewillperformarangescanontheinvoices_statusindextogettheROWIDsofallrowsinthe
invoicestablewiththedesiredstatus.ForeachROWIDfound,therecordfromtheinvoicestablewillbefetched.
Thissetofinvoicerecordswillbesetasideforamomentwhilethefocusturnstothecustomerstable.Here,Oraclewill
fetchallcustomersrecordswithafulltablescan.Toperformahashjoinbetweentheinvoicesandcustomerstables,
Oraclewillbuildahashfromthecustomerrecordsandusetheinvoicerecordstoprobethecustomerhash.
Next,anestedloopsjoinwillbeperformedbetweentheresultsofthehashjoinandtheinvoice_items_pkindex.Foreach
rowresultingfromthehashjoin,Oraclewillperformauniquescanoftheinvoice_items_pkindextofindindexentriesfor
matchinginvoiceitems.NotethatOraclegetseverythingitneedsfromtheindexanddoesntevenneedtoaccessthe
invoice_itemstableatall.Alsonotethatthenestedloopsoperationisanouterjoin.Asortoperationforthepurposesof
groupingisperformedontheresultsofthenestedloopsoperationinordertocompletetheSELECTstatement.
ItisinterestingtonotethatOraclechosetouseahashjoinandafulltablescanonthecustomerstableinsteadofthemore
traditionalnestedloopsjoin.Inthisdatabasetherearemanyinvoicesandarelativelysmallnumberofcustomers,making
afulltablescanofthecustomerstablelessexpensivethanrepeatedindexlookupsonthecustomers_pkindex.But
supposethecustomerstablewasenormousandtherelativenumberofinvoiceswasquitesmall.Inthatscenarioanested
loopsjoinmightbebetterthanahashjoin.ExaminingtheexecutionplanallowsyoutoseewhichjoinmethodOracleis
using.YoucouldthenapplyoptimizerhintstocoerceOracletousealternatemethodsandcomparetheperformance.
YoumaywonderhowIgotthatwholedetailedexplanationoutoftheeightlineexecutionplanlistingshownabove.DidI
readanythingintotheexecutionplan?No!Itsallthere!Understandingthestandardinputsandoutputsofeachtypeof
operationandcouplingthiswiththeindentingiskeytoreadinganexecutionplan.
Anestedloopsjoinoperationalwaystakestwoinputs:Foreveryrowcomingfromthefirstinput,thesecondinputis
executedoncetofindmatchingrows.Ahashjoinoperationalsotakestwoinputs:Thesecondinputisreadcompletely
onceandusedtobuildahash.Foreachrowcomingfromthefirstinput,oneprobeisperformedagainstthishash.Sorting
operations,meanwhile,takeinoneinput.Whentheentireinputhasbeenread,therowsaresortedandoutputinthe
desiredorder.
Nowletslookataquerywithamorecomplicatedexecutionplan:

SELECTcustomer_name
FROMcustomersa
http://www.dbspecialists.com/files/presentations/use_explain.html

16/23

2/27/2015

UseEXPLAINPLANandTKPROFToTuneYourApplications

WHEREEXISTS
(
SELECT1
FROMinvoices_viewb
WHEREb.customer_id=a.customer_id
ANDnumber_of_lines>100
)
ORDERBYcustomer_name;
IDPARENTOPERATIONOBJECT_NAME

0SELECTSTATEMENT
10SORTORDERBY
21FILTER
32TABLEACCESSFULLCUSTOMERS
42VIEWINVOICES_VIEW
54FILTER
65SORTGROUPBY
76NESTEDLOOPS
87TABLEACCESSBYINDEXROWIDINVOICES
98INDEXRANGESCANINVOICES_CUSTOMER_ID
107INDEXRANGESCANINVOICE_ITEMS_PK

Thisexecutionplanissomewhatcomplexbecausethequeryincludesasubquerythattheoptimizercouldnotrewriteasa
simplejoin,andaviewwhosedefinitioncouldnotbemergedintothequery.Thedefinitionoftheinvoices_viewviewisas
follows:

CREATEORREPLACEVIEWinvoices_view
AS
SELECTa.invoice_id,a.customer_id,a.invoice_date,a.invoice_status,
a.invoice_number,a.invoice_type,a.total_amount,
COUNT(*)number_of_lines
FROMinvoicesa,invoice_itemsb
WHEREb.invoice_id=a.invoice_id
GROUPBYa.invoice_id,a.customer_id,a.invoice_date,a.invoice_status,
a.invoice_number,a.invoice_type,a.total_amount;

Hereiswhatthisexecutionplansays:Oraclewillexecutethisquerybyreadingallrowsfromthecustomerstablewitha
fulltablescan.Foreachcustomerrecord,theinvoices_viewviewwillbeassembledasafilterandtherelevantcontentsof
theviewwillbeexaminedtodeterminewhetherthecustomershouldbepartoftheresultsetornot.
Oraclewillassembletheviewbyperforminganindexrangescanontheinvoices_customer_idindexandfetchingtherows
fromtheinvoicestablecontainingonespecificcustomer_id.Foreachinvoicerecordfound,theinvoice_items_pkindex
willberangescannedtogetanestedloopsjoinofinvoicestotheirinvoice_itemsrecords.Theresultsofthejoinare
sortedforgrouping,andthengroupswith100orfewerinvoice_itemsrecordsarefilteredout.
WhatisleftatthestepwithID4isalistofinvoicesforonespecificcustomerthathavemorethan100invoice_items
recordsassociated.Ifatleastonesuchinvoiceexists,thenthecustomerpassesthefilteratthestepwithID2.Finally,all
customerrecordspassingthisfilteraresortedforcorrectorderingandtheresultsarecomplete.
Notethatqueriesinvolvingsimpleviewswillnotresultinaviewoperationintheexecutionplan.ThisisbecauseOracle
canoftenmergeaviewdefinitionintothequeryreferencingtheviewsothatthetableaccessesrequiredtoimplementthe
viewjustbecomepartoftheregularexecutionplan.Inthisexample,theGROUPBYclauseembeddedintheviewfoiled
Oraclesabilitytomergetheviewintothequery,makingaseparateviewoperationnecessaryinordertoexecutethe
http://www.dbspecialists.com/files/presentations/use_explain.html

17/23

2/27/2015

UseEXPLAINPLANandTKPROFToTuneYourApplications

query.
Alsonotethatthefilteroperationcantakeonafewdifferentforms.Ingeneral,afilteroperationiswhereOraclelooksat
asetofcandidaterowsandeliminatessomebasedoncertaincriteria.Thiscriteriacouldinvolveasimpletestsuchas
number_of_lines>100oritcouldbeanelaboratesubquery.
Inthisexample,thefilteratstepID5takesonlyoneinput.HereOracleevaluateseachrowfromtheinputoneatatime
andeitheraddstherowtotheoutputordiscardsitasappropriate.Meanwhile,thefilteratstepID2takestwoinputs.
Whenafiltertakestwoinputs,Oraclereadstherowsfromthefirstinputoneatatimeandexecutesthesecondinput
onceforeachrow.Basedontheresultsofthesecondinput,therowfromthefirstinputiseitheraddedtotheoutputor
discarded.
Oracleisabletoperformsimplefilteringoperationswhileperformingafulltablescan.Therefore,aseparatefilter
operationwillnotappearintheexecutionplanwhenOracleperformsafulltablescanandthrowsoutrowsthatdont
satisfyaWHEREclause.FilteroperationswithoneinputcommonlyappearinquerieswithviewoperationsorHAVING
clauses,whilefilteroperationswithmultipleinputswillappearinquerieswithEXISTSclauses.
Animportantnoteaboutexecutionplansandsubqueries:WhenaSQLstatementinvolvessubqueries,Oracletriesto
mergethesubqueryintothemainstatementbyusingajoin.Ifthisisnotfeasibleandthesubquerydoesnothaveany
dependenciesorreferencestothemainquery,thenOraclewilltreatthesubqueryasacompletelyseparatestatement
fromthestandpointofdevelopinganexecutionplanalmostasiftwoseparateSQLstatementsweresenttothe
databaseserver.Whenyougenerateanexecutionplanforastatementthatincludesafullyautonomoussubquery,the
executionplanmaynotincludetheoperationsforthesubquery.Inthissituation,youneedtogenerateanexecutionplan
forthesubqueryseparately.

OtherColumnsinthePlanTable
Althoughtheplantablecontains24columns,sofarwehaveonlybeenusingsixoftheminourexecutionplanlistings.
Thesesixwillgetyouveryfarinthetuningprocess,butsomeoftheothercolumnscanbemildlyinterestingattimes.Still
othercolumnscanbeveryrelevantinspecificsituations.
Theoptimizercolumnintheplantableshowsthemode(suchasRULEorCHOOSE)usedbytheoptimizertogenerate
theexecutionplan.Thetimestampcolumnshowsthedateandtimethattheexecutionplanwasgenerated.Theremarks
columnisan80bytefieldwhereyoumayputyourowncommentsabouteachstepoftheexecutionplan.Youcan
populatetheremarkscolumnbyusinganordinaryUPDATEstatementagainsttheplantable.
Theobject_owner,object_node,andobject_instancecolumnscanhelpyoufurtherdistinguishthedatabaseobject
involvedintheoperation.Youmightlookattheobject_ownercolumn,forexample,ifobjectsinmultipleschemashave
thesamenameandyouarenotsurewhichoneisbeingreferencedintheexecutionplan.Theobject_nodeisrelevantin
distributedqueriesortransactions.Itindicatesthedatabaselinknametotheobjectiftheobjectresidesinaremote
database.Theobject_instancecolumnishelpfulinsituationssuchasaselfjoinwheremultipleinstancesofthesame
objectareusedinoneSQLstatement.
Thepartition_start,partition_stop,andpartition_idcolumnsofferadditionalinformationwhenapartitionedtableis
involvedintheexecutionplan.ThedistributioncolumngivesinformationabouthowthemultipleOracleprocesses
involvedinaparallelqueryorparallelDMLoperationinteractwitheachother.
Thecost,cardinality,andbytescolumnsshowestimatesmadebythecostbasedoptimizerastohowexpensivean
operationwillbe.RememberthattheexecutionplanisinsertedintotheplantablewithoutactuallyexecutingtheSQL
statement.Therefore,thesecolumnsreflectOraclesestimatesandnottheactualresourcesused.Whileitcanbeamusing
tolookattheoptimizerspredictions,sometimesyouneedtotakethemwithagrainofsalt.LaterwellseethatTKPROF
http://www.dbspecialists.com/files/presentations/use_explain.html

18/23

2/27/2015

UseEXPLAINPLANandTKPROFToTuneYourApplications

reportscanincludespecificinformationaboutactualresourcesusedateachstepoftheexecutionplan.
TheothercolumnintheplantableisawildcardwhereOraclecanstoreanysortoftextualinformationabouteachstep
ofanexecutionplan.Theother_tagcolumngivesanindicationofwhathasbeenplacedintheothercolumn.This
columnwillcontainvaluableinformationduringparallelqueriesanddistributedoperations.
ConsiderthefollowingdistributedqueryandoutputfromtheSQL*Plusautotracefacility:

SELECT/*+RULE*/
a.customer_number,a.customer_name,b.contact_id,b.contact_name
FROMcustomersa,contacts@sales.acme.comb
WHEREUPPER(b.contact_name)=UPPER(a.customer_name)
ORDERBYa.customer_number,b.contact_id;
ExecutionPlan

0SELECTSTATEMENTOptimizer=HINT:RULE
10SORT(ORDERBY)
21MERGEJOIN
32SORT(JOIN)
43REMOTE*SALES.ACME.COM
52SORT(JOIN)
65TABLEACCESS(FULL)OF'CUSTOMERS'
4SERIAL_FROM_REMOTESELECT"CONTACT_ID","CONTACT_NAME"FROM"CONTACTS""B

Intheexecutionplanhierarchy,thestepwithID4isdisplayedasaremoteoperationthroughthesales.acme.com
databaselink.AtthebottomoftheexecutionplanyoucanseetheactualSQLstatementthatthelocaldatabaseserver
sendstosales.acme.comtoperformtheremoteoperation.Thisinformationcamefromtheotherandother_tagcolumns
oftheplantable.
Hereishowtoreadthisexecutionplan:OracleobservedahintandusedtheRULEoptimizermodeinordertodevelop
theexecutionplan.First,aremotequerywillbesenttosales.acme.comtofetchthecontact_idsandnamesfromaremote
table.Thesefetchedrowswillbesortedforjoiningpurposesandtemporarilysetaside.Next,Oraclewillfetchallrecords
fromthecustomerstablewithafulltablescanandsortthemforjoiningpurposes.Next,thesetofcontactsandthesetof
customerswillbejoinedusingthemergejoinalgorithm.Finally,theresultsofthemergejoinwillbesortedforproper
orderingandtheresultswillbereturned.
Themergejoinoperationalwaystakestwoinputs,withtheprerequisitethateachinputhasalreadybeensortedonthe
joincolumnorcolumns.Themergejoinoperationreadsbothinputsintheirentiretyatonetimeandoutputstheresultsof
thejoin.Mergejoinsandhashjoinsareusuallymoreefficientthannestedloopsjoinswhenremotetablesareinvolved,
becausethesetypesofjoinswillalmostalwaysinvolvefewernetworkroundtrips.Hashjoinsarenotsupportedwhen
rulebasedoptimizationisused.BecauseoftheRULEhint,Oraclechoseamergejoin.

ReadingTKPROFReports
EveryTKPROFreportstartswithaheaderthatliststheTKPROFversion,thedateandtimethereportwasgenerated,
thenameofthetracefile,thesortoptionused,andabriefdefinitionofthecolumnheadingsinthereport.Everyreport
endswithaseriesofsummarystatistics.YoucanseetheheadingandsummarystatisticsonthesampleTKPROFreport
shownearlierinthispaper.
ThemainbodyoftheTKPROFreportconsistsofoneentryforeachdistinctSQLstatementthatwasexecutedbythe
http://www.dbspecialists.com/files/presentations/use_explain.html

19/23

2/27/2015

UseEXPLAINPLANandTKPROFToTuneYourApplications

databaseserverwhileSQLtracewasenabled.Thereareafewsubtletiesatplayintheprevioussentence.Ifan
applicationqueriesthecustomerstable50times,eachtimespecifyingadifferentcustomer_idasaliteral,thentherewillbe
50separateentriesintheTKPROFreport.Ifhowever,theapplicationspecifiesthecustomer_idasabindvariable,then
therewillbeonlyoneentryinthereportwithanindicationthatthestatementwasexecuted50times.Furthermore,the
reportwillalsoincludeSQLstatementsinitiatedbythedatabaseserveritselfinordertoperformsocalledrecursive
operationssuchasmanagethedatadictionaryanddictionarycache.
TheentriesforeachSQLstatementintheTKPROFreportareseparatedbyarowofasterisks.Thefirstpartofeach
entryliststheSQLstatementandstatisticspertainingtotheparsing,execution,andfetchingoftheSQLstatement.
Considerthefollowingexample:

********************************************************************************
SELECTtable_name
FROMuser_tables
ORDERBYtable_name
callcountcpuelapseddiskquerycurrentrows

Parse10.010.020000
Execute10.000.000000
Fetch140.590.990336330194

total160.601.010336330194
Missesinlibrarycacheduringparse:1
Optimizergoal:CHOOSE
Parsinguserid:RSCHRAG[recursivedepth:0]

Thismaynotseemlikeausefulexamplebecauseitissimplyaqueryagainstadictionaryviewanddoesnotinvolve
applicationtables.However,thisqueryactuallyservesthepurposewellfromthestandpointofhighlightingtheelementsof
aTKPROFreport.
Readingacross,weseethatwhileSQLtracewasenabled,theapplicationcalledonthedatabaseservertoparsethis
statementonce.0.01CPUsecondsoveraperiodof0.02elapsedsecondswereusedontheparsecall,althoughno
physicaldiskI/Osorevenanybuffergetswererequired.(Wecaninferthatalldictionarydatarequiredtoparsethe
statementwerealreadyinthedictionarycacheintheSGA.)
ThenextlineshowsthattheapplicationcalledonOracletoexecutethequeryonce,withlessthan0.01secondsofCPU
timeandelapsedtimebeingusedontheexecutecall.Again,nophysicaldiskI/Osorbuffergetswererequired.Thefact
thatalmostnoresourceswereusedontheexecutecallmightseemstrange,butitmakesperfectsensewhenyouconsider
thatOracledefersallworkonmostSELECTstatementsuntilthefirstrowisfetched.
Thenextlineindicatesthattheapplicationperformed14fetchcalls,retrievingatotalof194rows.The14callsuseda
totalof0.59CPUsecondsand0.99secondsofelapsedtime.AlthoughnophysicaldiskI/Oswereperformed,33,633
buffersweregotteninconsistentmode(consistentgets).Inotherwords,therewere33,633hitsinthebuffercacheand
nomisses.IranthisqueryfromSQL*Plus,andwecanseeherethatSQL*Plususesanarrayinterfacetofetchmultiple
rowsononefetchcall.Wecanalsoseethat,althoughnodiskI/Oswerenecessary,ittookquiteabitofprocessingto
completethisquery.
TheremaininglinesonthefirstpartoftheentryforthisSQLstatementshowthattherewasamissinthelibrarycache(the
SQLstatementwasnotalreadyinthesharedpool),theCHOOSEoptimizergoalwasusedtodeveloptheexecution
http://www.dbspecialists.com/files/presentations/use_explain.html

20/23

2/27/2015

UseEXPLAINPLANandTKPROFToTuneYourApplications

plan,andtheparsingwasperformedintheRSCHRAGschema.
Noticethetextinsquarebracketsconcerningrecursivedepth.ThisdidnotactuallyappearonthereportIaddeditfor
effect.Thefactthatthereportdidnotmentionrecursivedepthforthisstatementindicatesthatitwasexecutedatthetop
level.Inotherwords,theapplicationissuedthisstatementdirectlytothedatabaseserver.Whenrecursionisinvolved,the
TKPROFreportwillindicatethedepthoftherecursionnexttotheparsinguser.
Therearetwoprimarywaysinwhichrecursionoccurs.DatadictionaryoperationscancauserecursiveSQLoperations.
Whenaqueryreferencesaschemaobjectthatismissingfromthedictionarycache,arecursivequeryisexecutedinorder
tofetchtheobjectdefinitionintothedictionarycache.Forexample,aqueryfromaviewwhosedefinitionisnotinthe
dictionarycachewillcausearecursivequeryagainstview$tobeparsedintheSYSschema.Also,dynamicspace
allocationsindictionarymanagedtablespaceswillcauserecursiveupdatesagainstuet$andfet$intheSYSschema.
Useofdatabasetriggersandstoredprocedurescanalsocauserecursion.Supposeanapplicationinsertsarowintoa
tablethathasadatabasetrigger.Whenthetriggerfires,itsstatementsrunatarecursiondepthofone.Ifthetrigger
invokesastoredprocedure,therecursiondepthcouldincreasetotwo.Thiscouldcontinuethroughanynumberoflevels.
SofarwehavebeenlookingatthetoppartoftheSQLstatemententryintheTKPROFreport.Theremainderofthe
entryconsistsofarowsourceoperationlistandoptionallyanexecutionplandisplay.(Iftheexplainkeywordwasnot
usedwhentheTKPROFreportwasgenerated,thentheexecutionplandisplaywillbeomitted.)Considerthefollowing
example,whichistherestoftheentryshownabove:

RowsRowSourceOperation

194SORTORDERBY
194NESTEDLOOPS
195NESTEDLOOPSOUTER
195NESTEDLOOPSOUTER
195NESTEDLOOPS
11146TABLEACCESSBYINDEXROWIDOBJ$
11146INDEXRANGESCAN(objectid34)
11339TABLEACCESSCLUSTERTAB$
12665INDEXUNIQUESCAN(objectid3)
33INDEXUNIQUESCAN(objectid33)
193TABLEACCESSCLUSTERSEG$
387INDEXUNIQUESCAN(objectid9)
194TABLEACCESSCLUSTERTS$
388INDEXUNIQUESCAN(objectid7)
RowsExecutionPlan

0SELECTSTATEMENTGOAL:CHOOSE
194SORT(ORDERBY)
194NESTEDLOOPS
195NESTEDLOOPS(OUTER)
195NESTEDLOOPS(OUTER)
195NESTEDLOOPS
11146TABLEACCESS(BYINDEXROWID)OF'OBJ$'
11146INDEX(RANGESCAN)OF'I_OBJ2'(UNIQUE)
11339TABLEACCESS(CLUSTER)OF'TAB$'
12665INDEX(UNIQUESCAN)OF'I_OBJ#'(NONUNIQUE)
33INDEX(UNIQUESCAN)OF'I_OBJ1'(UNIQUE)
193TABLEACCESS(CLUSTER)OF'SEG$'
387INDEX(UNIQUESCAN)OF'I_FILE#_BLOCK#'(NONUNIQUE)
194TABLEACCESS(CLUSTER)OF'TS$'
388INDEX(UNIQUESCAN)OF'I_TS#'(NONUNIQUE)
http://www.dbspecialists.com/files/presentations/use_explain.html

21/23

2/27/2015

UseEXPLAINPLANandTKPROFToTuneYourApplications

Therowsourceoperationlistinglooksverymuchlikeanexecutionplan.ItisbasedondatacollectedfromtheSQLtrace
fileandcanbethoughtofasapoormansexecutionplan.Itisclose,butnotcomplete.
TheexecutionplanshowsthesamebasicinformationyoucouldgetfromtheautotracefacilityofSQL*Plusorby
queryingtheplantableafteranEXPLAINPLANstatementwithonekeydifference.Therowscolumnalongtheleft
sideoftheexecutionplancontainsacountofhowmanyrowsofdataOracleprocessedateachstepduringtheexecution
ofthestatement.Thisisnotanestimatefromtheoptimizer,butratheractualcountsbasedonthecontentsoftheSQL
tracefile.
Althoughthequeryinthisexamplegoesagainstadictionaryviewandisnotterriblyinteresting,youcanseethatOracle
didalotofworktogetthe194rowsintheresult:11,146rangescanswereperformedagainstthei_obj2index,followed
by11,146accessesontheobj$table.Thisledto12,665nonuniquelookupsonthei_obj#index,11,339accesseson
thetab$table,andsoon.
InsituationswhereitisfeasibletoactuallyexecutetheSQLstatementyouwishtoexplain(asopposedtomerelyparsing
itaswiththeEXPLAINPLANstatement),IbelieveTKPROFoffersthebestexecutionplandisplay.GUItoolssuchas
TOADwillgiveyouresultswithmuchlesseffort,butthedisplayyougetfromTOADisnot100%completeandin
certainsituationscriticalinformationismissing.(Again,myexperienceiswiththefreeversion!)Meanwhile,simpleplan
tablequeryscriptslikemyexplain.sqlpresentedearlierinthispaperorutlxpls.sqldisplayveryincompleteinformation.
TKPROFgivesthemostrelevantdetail,andtheactualrowcountsoneachoperationcanbeveryusefulindiagnosing
performanceproblems.AutotraceinSQL*Plusgivesyoumostoftheinformationandiseasytouse,soIgiveitaclose
secondplace.

TKPROFReports:MoreThanJustExecutionPlans
TheinformationdisplayedinaTKPROFreportcanbeextremelyvaluableintheapplicationtuningprocess.Ofcoursethe
executionplanlistingwillgiveyouinsightsintohowOracleexecutestheSQLstatementsthatmakeuptheapplication,and
waystopotentiallyimproveperformance.However,theotherelementsoftheTKPROFreportcanbehelpfulaswell.
LookingattherepetitionofSQLstatementsandthelibrarycachemissstatistics,youcandetermineiftheapplicationis
makingappropriateuseofOraclessharedSQLfacility.Arebindvariablesbeingused,oriseveryqueryaunique
statementthatmustbeparsedfromscratch?
Fromthecountsofparse,execute,andfetchcalls,youcanseeifapplicationsaremakingappropriateuseofOracles
APIs.Istheapplicationfetchingrowsoneatatime?Istheapplicationreparsingthesamecursorthousandsoftimes
insteadofholdingitopenandavoidingsubsequentparses?IstheapplicationsubmittinglargenumbersofsimpleSQL
statementsinsteadofbulkingthemintoPL/SQLblocksorperhapsusingarraybinds?
LookingattheCPUandI/Ostatistics,youcanseewhichstatementsconsumethemostsystemresources.Couldsome
statementsbetunedsoastobelessCPUintensiveorlessI/Ointensive?Wouldshavingjustafewbuffergetsoffofa
statementsexecutionplanhaveabigimpactbecausethestatementgetsexecutedsofrequently?
Therowcountsontheindividualoperationsinanexecutionplandisplaycanhelpidentifyinefficiencies.Aretablesbeing
joinedinthewrongorder,causinglargenumbersofrowstobejoinedandeliminatedonlyattheveryend?Arelarge
numbersofduplicaterowsbeingfedintosortsforuniquenesswhenperhapstheduplicatescouldhavebeenweededout
earlieron?
TKPROFreportsmayseemlongandcomplicated,butnothinginthereportiswithoutpurpose.(Well,okay,therow
sourceoperationlistingsometimesisntveryuseful!)Youcanlearnvolumesabouthowyourapplicationinteractswiththe
databaseserverbygeneratingandreadingaTKPROFreport.
http://www.dbspecialists.com/files/presentations/use_explain.html

22/23

2/27/2015

UseEXPLAINPLANandTKPROFToTuneYourApplications

Conclusion
InthispaperwehavediscussedhowtogenerateexecutionplansandTKPROFreports,andhowtointerpretthem.
Wevewalkedthroughseveralexamplesinordertoclarifythetechniquespresented.Whenyouhaveafirmunderstanding
ofhowtheOracledatabaseserverexecutesyourSQLstatementsandwhatresourcesarerequiredeachstepoftheway,
youhavetheabilitytofindbottlenecksandtuneyourapplicationsforpeakperformance.EXPLAINPLANand
TKPROFgiveyoutheinformationyouneedforthisprocess.
Whenisafulltablescanbetterthananindexrangescan?Whenisanestedloopsjoinbetterthanahashjoin?Inwhich
ordershouldtablesbejoined?Theseareallquestionswithoutuniversalanswers.Inreality,therearemanyfactorsthat
contributetodeterminingwhichjoinmethodisbetterorwhichjoinorderisoptimal.
Inthispaperwehavelookedatthetoolsthatgiveyoutheinformationyouneedtomaketuningdecisions.Howto
translateanexecutionplanorTKPROFreportintoanactionplantoachievebetterperformanceisnotsomethingthatcan
betaughtinonepaper.Youwillneedtoreadseveralpapersorbooksinordertogiveyourselfsomebackgroundonthe
subject,andthenyouwillneedtotrypotentialsolutionsinatestenvironmentandevaluatethem.Ifyoudoenough
applicationtuning,youwilldevelopanintuitionforspottingperformanceproblemsandpotentialsolutions.Thisintuition
comesfromlotsofexperience,andyoucantgainitsolelyfromreadingpapersorbooks.
FormoreinformationabouttheEXPLAINPLANfacility,executionplansingeneral,andTKPROF,consulttheOracle
manualentitledOracle8iDesigningandTuningforPerformance.Tolearnmoreaboutapplicationtuningtechniques,I
suggestyoupickupRichardNiemiecstomeonthesubject,OraclePerformanceTuningTips&Techniques,available
fromOraclePress.

AbouttheAuthor
RogerSchraghasbeenanOracleDBAandapplicationarchitectforoverelevenyears,startingoutatOracleCorporation
ontheOracleFinancialsdevelopmentteam.HeisthefounderofDatabaseSpecialists,Inc.,aconsultinggroup
specializinginbusinesssolutionsbasedonOracletechnology.YoucanvisitDatabaseSpecialistsonthewebat
http://www.dbspecialists.com,andyoucanreachRogerbycalling+1.415.344.0500orviaemailat
rschrag@dbspecialists.com.
Still Looking for Help on this Subject?
Get a Consultation
We would be happy to talk with you about our services and how our seniorlevel database team might help
you. Call Database Specialists at 4153440500 or 8886480500 or fill out a free consultation request
form.
Complimentary Newsletter
If you'd like to receive our complimentary monthly newsletter with database tips and new white paper
announcements, sign up for The Specialist.

Copyright2005DatabaseSpecialists,Inc.http://www.dbspecialists.com

http://www.dbspecialists.com/files/presentations/use_explain.html

23/23

You might also like