Tải bản đầy đủ
Lab 20.3: Optimizing Function Execution in SQL

Lab 20.3: Optimizing Function Execution in SQL

Tải bản đầy đủ

PL/SQLengines.Beforedecidingwhichapproachtouse,itisadvisabletodoacost
analysisandweighthebenefitsagainstthepossibleneedtoreusethefunctioninother
contexts.

CreatingaFunctionwiththeUDFPragma
FunctionscanbecreatedbyaddingtheUDFpragmasyntax,whichnotifiesthecompiler
thatauser-definedfunctionwillbeusedinSQLstatements.Apragmaisbasicallyahintto
thecompilerthatallowsittooptimizethefunctionappropriately.WhentheUDFpragma
syntaxisused,thefunctionwillhavehigherperformancewhenusedinSQL.Verylittle
needstobedonetoapplythepragma,excepttoaddthephrasepragmaUDFpriortothe
variabledeclaration,asshowninboldinthefollowingexample:
ForExamplech20_7.sql
Clickheretoviewcodeimage
CREATEORREPLACEFUNCTIONshow_description
(i_course_nocourse.course_no%TYPE)
RETURNvarchar2
AS
pragmaUDF;
v_descriptionvarchar2(50);
BEGIN
SELECTdescription
INTOv_description
FROMcourse
WHEREcourse_no=i_course_no;
RETURNv_description;
EXCEPTION
WHENNO_DATA_FOUND
THEN
RETURN(‘TheCourseisnotinthedatabase’);
WHENOTHERS
THEN
RETURN(‘Errorinrunningshow_description’);
END;

Summary
Inthischapter,youlearnedhowtocreateandexecutefunctions.Youalsolearnedhowto
includefunctionsinSQLstatements.Finally,youlearnedtwomethodstooptimize
functionexecutioninSQL:useoftheWITHfunctionanduseofthepragmaUDFsyntax.
BytheWay
Thecompanionwebsiteprovidesadditionalexercisesandsuggestedanswers
forthischapter,withdiscussionrelatedtohowthoseanswersresulted.The
mainpurposeoftheseexercisesistohelpyoutestthedepthofyour
understandingbyutilizingalloftheskillsthatyouhaveacquiredthroughout
thischapter.

21.Packages
Inthischapter,youwilllearnabout
CreatingPackages
CursorsVariables
ExtendingthePackage
PackageInstantiationandInitialization
SERIALLY_REUSABLEPackages
ApackageisacollectionofPL/SQLobjectsgroupedtogetherunderonepackagename.
Packagesmayincludeprocedures,functions,cursors,declarations,types,andvariables.
Collectingobjectsintoapackagehasnumerousbenefits.Inthischapter,youwilllearn
whatthesebenefitsareandhowtotakeadvantageofthem.

Lab21.1:CreatingPackages
Afterthislab,youwillbeableto
CreatePackageSpecifications
CreatePackageBodies
CallStoredPackages
CreatePrivateObjects
Therearenumerousbenefitsofusingpackagesasamethodtobundleyourfunctionsand
procedures,thefirstbeingthatawell-designedpackageisalogicalgroupingofobjects
suchasfunctions,procedures,globalvariables,andcursors.Allofthecode(parsetreeand
pseudocode[p-code])isloadedintomemory(sharedglobalarea[SGA]oftheOracle
server)onthefirstcallofthepackage.Thismeansthatthefirstcalltothepackageisvery
expensive(itinvolvesalotofprocessingontheserver),butallsubsequentcallswillresult
inimprovedperformance.Forthisreason,packagesareoftenusedinapplicationswhere
proceduresandfunctionsarecalledrepeatedly.

ExampleofaBasicCurrencyConversion
Onceyouhavethesamecalculationwritteninmultipleplaces,youhavea
largemaintenancejobeverytimethecalculationinenhancedincomplexity.
Forexample,basiccurrencyconversionisfairlysimple:Anamountis
multipliedbyanexchangerate.Inactuality,currencyconversionhasbecome
morecomplex.OncetheEuropeanUnionwasformed,individualnational
currencieswerephasedoutwhenacountryadoptedtheeuroasitscurrency.
TheEuropeanUnionthenadoptedacomplexpolicyonhowthese“dead”
currencieswouldbeconverted.Thisconsiderationwouldbeimportantif
contractsweresetupwhenthecurrencywasinplacebutlaterthecurrency
wasphasedout.IfyouhadanoldcontractinGermandeutschemarksthat
neededtobeconvertedintoU.S.dollars,forexample,itwouldhavetogo
throughthisprocess.FirstitwouldbeconvertedfromGermandeutschemarks
toeurosbasedontheprevailingrate.Thenitwouldberoundedbasedona
standardroundingmechanismforGermandeutschemarkstoeuros,andthen
itwouldbeconvertedfromeurostoU.S.dollarsattheprevailingrate.Ifyour
programshadmanyplaceswherecurrencywasconverted,itwouldmake
moresensetoencapsulatetheconversionprocessintoonefunctionthat
encompassedthiseuroscenario.Thisfunctioncouldbeapublicorprivate
(explainedlaterinthischapter)functionthatallotherproceduresinthesame
packagecalled.
Packagesallowyoutomakeuseofsomeoftheconceptsinvolvedinobject-oriented
programming,eventhoughPL/SQLisnota“true”object-orientedprogramminglanguage.
WiththePL/SQLpackage,youcancollectfunctionsandproceduresandprovidethem
withacontext.Becauseallthepackagecodeisloadedintomemory,youcanalsowrite
yourcodesothatsimilarcodeisplacedintothepackageinamannerthatallowsmultiple
proceduresandfunctionstocallthem.Youwouldwanttodothisifthelogicfor
calculationisfairlyintensiveandyouwanttokeepitinoneplace.

CreatingPackageSpecifications
Anadditionallevelofsecurityapplieswhenusingpackages.Whenauserexecutesa
procedureinapackage(orstoredproceduresandfunctions),theprocedureoperateswith
thesamepermissionsasitsowner.Packagesallowthecreationofprivatefunctionsand
procedures,whichcanbecalledonlyfromotherfunctionsandproceduresinthepackage.
Thisenforcesinformationhiding.Thestructureofthepackagethusencouragestop-down
design.

ThePackageSpecification
Thepackagespecificationcontainsinformationaboutthecontentsofthepackage,butnot
thecodefortheproceduresandfunctions.Italsocontainsdeclarationsofglobal/public
variables.AnythingplacedinthedeclarationsectionofaPL/SQLblockmaybecodedina
packagespecification.Allobjectsplacedinthepackagespecificationarecalledpublic
objects.Anyfunctionorprocedurenotinthepackagespecificationbutcodedinapackage
bodyiscalledaprivatefunctionorprocedure.
Whenpublicproceduresandfunctionsarebeingcalledfromapackage,theprogrammer
writingthe“calling”processneedsonlytheinformationinthepackagespecification,asit
providesalltheinformationneededtocalloneoftheproceduresorfunctionswithinthe
package.Thesyntaxforthepackagespecificationisasfollows:
Clickheretoviewcodeimage
PACKAGEpackage_name
IS
[declarationsofvariablesandtypes]
[specificationsofcursors]
[specificationsofmodules]
END[package_name];

ThePackageBody
Thepackagebodycontainstheactualexecutablecodefortheobjectsdescribedinthe
packagespecification.Itcontainsthecodeforallproceduresandfunctionsdescribedin
thespecificationandmayadditionallycontaincodeforobjectsnotdeclaredinthe
specification;thelattertypeofpackagedobjectisinvisibleoutsidethepackageandis
referredtoas“hidden.”Whencreatingstoredpackages,thepackagespecificationand
bodycanbecompiledseparately.
Clickheretoviewcodeimage
PACKAGEBODYpackage_name
IS
[declarationsofvariablesandtypes]
[specificationandSELECTstatementofcursors]
[specificationandbodyofmodules]
[BEGIN
executablestatements]
[EXCEPTION
exceptionhandlers]
END[package_name];

RulesforthePackageBody
Anumberofrulesmustbefollowedinpackagebodycode.First,theremustbeanexact
matchbetweenthecursorandmoduleheadersandtheirdefinitionsinpackage
specification.Second,declarationsofvariables,exceptions,type,orconstantsinthe
specificationcannotberepeatedinthebody.Third,anyelementdeclaredinthe
specificationcanbereferencedinthebody.

ReferencingPackageElements
Youusethefollowingnotationwhencallingpackagedelementsfromoutsidethepackage:
package_name.element.
Youdonotneedtoqualifyelementswhentheyaredeclaredandreferencedinsidethe
bodyofthepackageorwhentheyaredeclaredinaspecificationandreferencedinsidethe
bodyofthesamepackage.
Thefollowingexampleshowsthepackagespecificationforthepackage
manage_students.Laterinthischapter,asectionwilldescribethecreationofthe
bodyofthesamepackage.
ForExamplech21_1.sql
Clickheretoviewcodeimage
1CREATEORREPLACEPACKAGEmanage_students
2AS
3PROCEDUREfind_sname
4(i_student_idINstudent.student_id%TYPE,
5o_first_nameOUTstudent.first_name%TYPE,
6o_last_nameOUTstudent.last_name%TYPE
7);
8FUNCTIONid_is_good
9(i_student_idINstudent.student_id%TYPE)
10RETURNBOOLEAN;
11ENDmanage_students;

Uponrunningthisscript,thespecificationforthepackagemanage_studentswill
becompiledintothedatabase.Thespecificationforthepackagenowindicatesthatthere
isoneprocedureandonefunction.Theprocedurefind_snamerequiresoneIN
parameter,thestudentID;itreturnstwoOUTparameters,thestudent’sfirstnameandthe
student’slastname.Thefunctionid_is_goodtakesinasingleparameter,astudentID,
andreturnsaBooleanvalue(trueorfalse).Althoughthebodyhasnotyetbeenentered
intothedatabase,thepackageisstillavailableforotherapplications.Forexample,ifyou
includedacalltooneoftheseproceduresinanotherstoredprocedure,thatprocedure
wouldcompile(butwouldnotexecute).Thisisillustratedbythefollowingexample.
ForExamplech21_2.sql
Clickheretoviewcodeimage
SETSERVEROUTPUTON
DECLARE
v_first_namestudent.first_name%TYPE;
v_last_namestudent.last_name%TYPE;
BEGIN
manage_students.find_sname
(125,v_first_name,v_last_name);
DBMS_OUTPUT.PUT_LINE(v_first_name||’‘||v_last_name);
END;

Thisprocedurecannotrunbecauseonlythespecificationfortheprocedureexistsinthe
database,notthebody.TheSQL*Plussessionreturnsthefollowingoutput:
Clickheretoviewcodeimage
ERRORatline1:

ORA-04068:existingstateofpackageshasbeendiscarded
ORA-04067:notexecuted,packagebody
“STUDENT.MANAGE_STUDENTS”doesnotexist
ORA-06508:PL/SQL:couldnotfindprogram
unitbeingcalled
ORA-06512:atline5

Thefollowingexamplecreatesapackagespecificationforapackagenamed
school_api.Thispackagecontainstheprocedurediscount_costfromChapter19
andthefunctionnew_instructor_idfromChapter20.
ForExamplech21_3.sql
Clickheretoviewcodeimage
CREATEORREPLACEPACKAGEschool_apias
PROCEDUREdiscount_cost;
FUNCTIONnew_instructor_id
RETURNinstructor.instructor_id%TYPE;
ENDschool_api;

CreatingPackageBodies
Nowwewillcreatethebodyofthemanage_studentsandschool_apipackages,
whichwerespecifiedintheprevioussection.
ForExamplech21_4.sql
Clickheretoviewcodeimage
1CREATEORREPLACEPACKAGEBODYmanage_students
2AS
3PROCEDUREfind_sname
4(i_student_idINstudent.student_id%TYPE,
5o_first_nameOUTstudent.first_name%TYPE,
6o_last_nameOUTstudent.last_name%TYPE
7)
8IS
9v_student_idstudent.student_id%TYPE;
10BEGIN
11SELECTfirst_name,last_name
12INTOo_first_name,o_last_name
13FROMstudent
14WHEREstudent_id=i_student_id;
15EXCEPTION
16WHENOTHERS
17THEN
18DBMS_OUTPUT.PUT_LINE
19(‘Errorinfindingstudent_id:‘||v_student_id);
20ENDfind_sname;
21FUNCTIONid_is_good
22(i_student_idINstudent.student_id%TYPE)
23RETURNBOOLEAN
24IS
25v_id_cntnumber;
26BEGIN
27SELECTCOUNT(*)
28INTOv_id_cnt
29FROMstudent
30WHEREstudent_id=i_student_id;

31RETURN1=v_id_cnt;
32EXCEPTION
33WHENOTHERS
34THEN
35RETURNFALSE;
36ENDid_is_good;
37ENDmanage_students;

Thisscriptcompilesthepackagemanage_studentsintothedatabase.The
specificationforthepackageindicatesthatthereisoneprocedureandonefunction.The
procedurefind_snamerequiresoneINparameter,thestudentID;itreturnstwoOUT
parameters,thestudent’sfirstnameandthestudent’slastname.Thefunction
id_is_goodtakesinasingleparameterofastudentIDandreturnsaBooleanvalue
(trueorfalse).Althoughthebodyhasnotyetbeenenteredintothedatabase,thepackage
isstillavailableforotherapplications.Forexample,ifyouincludedacalltooneofthese
proceduresinanotherstoredprocedure,thatprocedurewouldcompile(butwouldnot
execute).
Thenextexamplecreatesthepackagebodyforthepackagenamedschool_apithat
wascreatedinthepreviousexample.Itcontainstheprocedurediscount_costfrom
Chapter19andthefunctionnew_instructor_idfromChapter20.
ForExamplech21_5.sql
Clickheretoviewcodeimage
1CREATEORREPLACEPACKAGEBODYschool_apiAS
2PROCEDUREdiscount_cost
3IS
4CURSORc_group_discount
5IS
6SELECTdistincts.course_no,c.description
7FROMsections,enrollmente,coursec
8WHEREs.section_id=e.section_id
9GROUPBYs.course_no,c.description,
10e.section_id,s.section_id
11HAVINGCOUNT(*)>=8;
12BEGIN
14FORr_group_discountINc_group_discount
14LOOP
15UPDATEcourse
16SETcost=cost*.95
17WHEREcourse_no=r_group_discount.course_no;
18DBMS_OUTPUT.PUT_LINE
19(‘A5%discounthasbeengivento’
20||r_group_discount.course_no||’
21’||r_group_discount.description);
22ENDLOOP;
23ENDdiscount_cost;
24FUNCTIONnew_instructor_id
25RETURNinstructor.instructor_id%TYPE
26IS
27v_new_instidinstructor.instructor_id%TYPE;
28BEGIN
29SELECTINSTRUCTOR_ID_SEQ.NEXTVAL
30INTOv_new_instid
31FROMdual;

32RETURNv_new_instid;
33EXCEPTION
34WHENOTHERS
35THEN
36DECLARE
37v_sqlerrmVARCHAR2(250):=
SUBSTR(SQLERRM,1,250);
38BEGIN
39RAISE_APPLICATION_ERROR(-20003,
40‘Errorininstructor_id:‘||v_sqlerrm);
41END;
42ENDnew_instructor_id;
43ENDschool_api;

CallingStoredPackages
Nowwewilluseelementsofthemanage_studentspackageinanothercodeblock.
ForExamplech21_6.sql
Clickheretoviewcodeimage
SETSERVEROUTPUTON
DECLARE
v_first_namestudent.first_name%TYPE;
v_last_namestudent.last_name%TYPE;
BEGIN
IFmanage_students.id_is_good(&&v_id)
THEN
manage_students.find_sname(&&v_id,v_first_name,
v_last_name);
DBMS_OUTPUT.PUT_LINE(‘StudentNo.‘||&&v_id||’is‘
||v_last_name||’,‘||v_first_name);
ELSE
DBMS_OUTPUT.PUT_LINE
(‘StudentID:‘||&&v_id||’isnotinthedatabase.’);
ENDIF;
END;

ThisisacorrectPL/SQLblockforrunningthefunctionandtheprocedureinthe
packagemanage_students.Ifanexistingstudent_idisentered,thenthenameof
thestudentisdisplayed.IfthestudentIDisnotvalid,thenanerrormessageisdisplayed.
Thefollowingexampleshowstheresultwhen145isenteredforthevariablev_idin
SQLDeveloper.Thescriptoutputshowstheoriginalscriptandthenthescriptonceall
variableshavebeenreplacedwiththenumberentered(inthiscase145).Thefinalline(in
bold)istheresult.
Clickheretoviewcodeimage
old:DECLARE
v_first_namestudent.first_name%TYPE;
v_last_namestudent.last_name%TYPE;
BEGIN
IFmanage_students.id_is_good(&&v_id)
THEN
manage_students.find_sname(&&v_id,v_first_name,
v_last_name);
DBMS_OUTPUT.PUT_LINE(‘StudentNo.‘||&&v_id||’is‘
||v_last_name||’,‘||v_first_name);

ELSE
DBMS_OUTPUT.PUT_LINE
(‘StudentID:‘||&&v_id||’isnotinthedatabase.’);
ENDIF;
END;
new:DECLARE
v_first_namestudent.first_name%TYPE;
v_last_namestudent.last_name%TYPE;
BEGIN
IFmanage_students.id_is_good(145)
THEN
manage_students.find_sname(145,v_first_name,
v_last_name);
DBMS_OUTPUT.PUT_LINE(‘StudentNo.‘||145||’is‘
||v_last_name||’,‘||v_first_name);
ELSE
DBMS_OUTPUT.PUT_LINE
(‘StudentID:‘||145||’isnotinthedatabase.’);
ENDIF;
END;
anonymousblockcompleted
StudentNo.145isLefkowitz,Paul

Thefunctionid_is_goodreturnsTRUEforanexistingstudent_idsuchas145.
ControlthenpassestothefirstpartoftheIFstatementandtheprocedure
manage_students.find_snamefindsthefirstandlastnamesforstudent_idof
145—specifically,PaulLefkowitz.
Thefollowingisanexampleofatestscriptfortheschool_apipackage.
ForExamplech21_7.sql
Clickheretoviewcodeimage
SETSERVEROUTPUTON
DECLARE
V_instructor_idinstructor.instructor_id%TYPE;
BEGIN
School_api.Discount_Cost;
v_instructor_id:=school_api.new_instructor_id;
DBMS_OUTPUT.PUT_LINE
(‘Thenewidis:‘||v_instructor_id);
END;

CreatingPrivateObjects
Publicelementsareelementsdefinedinthepackagespecification.Ifanobjectisdefined
onlyinthepackagebody,thenitisprivate.Privateelementscannotbeaccesseddirectly
byanyprogramsoutsidethepackage.Youcanthinkofthepackagespecificationasbeing
a“menu”ofpackageditemsthatareavailabletousers;theremaybeotherobjects
workingbehindthescenes,buttheyaren’taccessible.Theycannotbecalledorutilizedin
anyway;theyareavailableaspartoftheinternal“menu”ofthepackageandcanbecalled
onlybyotherelementsofthepackage.
Thefollowingstepsshowhowtotransformthepackagemanage_studentssothat
thefunctionstudent_count_privbecomesaprivatefunction.Thepublicprocedure

display_student_countthencallsthisprivatefunction.
Step1:Replacethelastlinesofthemanage_studentspackagespecificationwith
thefollowingcodeandrecompilethepackagespecification:
Clickheretoviewcodeimage
11PROCEDUREdisplay_student_count;
12ENDmanage_students;

Step2:Replacetheendofthebodywiththefollowingcodeandrecompilethepackage
body.Lines1–36areunchangedfromlines1–36inexamplech21_4.sql:
Clickheretoviewcodeimage
37FUNCTIONstudent_count_priv
38RETURNNUMBER
39IS
40v_countNUMBER;
41BEGIN
42selectcount(*)
43intov_count
44fromstudent;
45returnv_count;
46EXCEPTION
47WHENOTHERS
48THEN
49return(0);
50ENDstudent_count_priv;
51PROCEDUREdisplay_student_count
52is
53v_countNUMBER;
54BEGIN
55v_count:=student_count_priv;
56DBMS_OUTPUT.PUT_LINE
57(‘Thereare‘||v_count||’students.’);
58ENDdisplay_student_count;
59ENDmanage_students;

Nowrunthefollowingscript:
Clickheretoviewcodeimage
DECLARE
V_countNUMBER;
BEGIN
V_count:=Manage_students.student_count_priv;
DBMS_OUTPUT.PUT_LINE(v_count);
END;

Becausetheprivatefunctionstudent_count_privcannotbecalledfromoutsidethe
package,youwillreceivethefollowingerrormessage:
Clickheretoviewcodeimage
ERRORatline1:
ORA-06550:line4,column31:
PLS-00302:component‘STUDENT_COUNT_PRIV’mustbedeclared
ORA-06550:line4,column3:
PL/SQL:Statementignored

Itappearsasiftheprivatefunctiondoesnotexist.Thispointisimportanttokeepin

mind—itcanbeusefulwhenyouarewritingPL/SQLpackagesusedbyotherdevelopers.
Thosedevelopersneedtoseeonlythepackagespecification,nottheinnerworkingsofthe
package.Thatis,theyneedtoknowwhatisbeingpassedintotheproceduresand
functionsandwhatisbeingreturned.Ifanumberofprocedureswillmakeuseofthesame
logic,itmaymakemoresensetoputthatlogicintoaprivatefunctioncalledbythe
procedures.Thisisalsoagoodapproachtokeepinmindifonecalculationwillbeusedby
manyotherproceduresinthesamepackage.Forexample,wejustcreatedafunctionto
countstudents.Perhapsotherprocedureswillneedtomakeuseofthisfunction—suchas
ifachangeinthepriceofallcoursesshouldoccuroncethestudentcountreachesacertain
number.
Thefollowingexampleshowsavalidmethodofrunningaprocedure.Theresultwould
bealineindicatingthenumberofstudentsinthedatabase.Notethattheprocedureinthe
packagemanage_studentsusestheprivatefunctionstudent_count_privto
retrievethestudentcount.
Clickheretoviewcodeimage
SETSERVEROUTPUTON
Executemanage_students.display_student_count;

Ifyouforgettoincludeaprocedureorfunctioninapackagespecification,itbecomes
private.Ifyoudeclareaprocedureorfunctioninthepackagespecification,butthendonot
defineitwhenyoucreatethebody,youwillreceivethefollowingerrormessage:
Clickheretoviewcodeimage
PLS-00323:subprogramorcursor‘procedure_name’is
declaredinapackagespecificationandmustbe
definedinthepackagebody

Thefollowingupdatedscriptforthemanage_studentspackageaddsaprivate
functiontotheschool_apicalledget_course_descript_private.Itacceptsa
course.course_no%TYPEandreturnsacourse.description%TYPE.It
searchesforandreturnsthecoursedescriptionforthecoursenumberpassedtoit.Ifthe
coursedoesnotexistorifanerroroccurs,itreturnsNULL.Nothingneedstobeaddedto
thepackagespecification,becauseyouaresimplyaddingaprivateobject.
ForExamplech21_8.sql
Clickheretoviewcodeimage
CREATEORREPLACEPACKAGEmanage_students
AS
PROCEDUREfind_sname
(i_student_idINstudent.student_id%TYPE,
o_first_nameOUTstudent.first_name%TYPE,
o_last_nameOUTstudent.last_name%TYPE
);
FUNCTIONid_is_good
(i_student_idINstudent.student_id%TYPE)
RETURNBOOLEAN;
PROCEDUREdisplay_student_count;
ENDmanage_students;

Thepackagebodyformanage_studentsnowhasthefollowingform: