Professional Documents
Culture Documents
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