Tải bản đầy đủ
Lab 18.3: Binding Collections in SQL Statements

Lab 18.3: Binding Collections in SQL Statements

Tải bản đầy đủ

/
CREATEORREPLACEPACKAGEBODYtest_adm_pkg
AS
PROCEDUREpopulate_test(row_num_tabROW_NUM_TYPE
,row_num_typeROW_TEXT_TYPE)
IS
BEGIN
FORALLiIN1..10
INSERTINTOtest(row_num,row_text)
VALUES(row_num_tab(i),row_num_type(i));
ENDpopulate_test;
PROCEDUREupdate_test(row_num_tabROW_NUM_TYPE
,row_num_typeROW_TEXT_TYPE)
IS
BEGIN
FORALLiIN1..10
UPDATEtest
SETrow_text=row_num_type(i)
WHERErow_num=row_num_tab(i);
ENDupdate_test;
PROCEDUREdelete_test(row_num_tabROW_NUM_TYPE)
IS
BEGIN
FORALLiIN1..10
DELETEfromtest
WHERErow_num=row_num_tab(i);
ENDdelete_test;
ENDtest_adm_pkg;
/

Thispackagehasbothapackagespecificationandapackagebody.Thepackage
specificationcontainsdeclarationsoftwoassociativearraytypes(ROW_NUM_TYPEand
ROW_TEXT_TYPE)andthreeprocedures(POPULATE_TEST,UPDATE_TEST,and
DELETE_TEST).EachprocedurehasparametersROW_NUM_TABandROW_TEXT_TAB
thatarebasedonthecollectiontypesdefinedinthispackage.Thepackagebodycontains
thecodefortheproceduresdeclaredinthepackagespecification.
Thefollowingexampleusesthisnewlycreatedpackage.Referencestothepackage
objectsarehighlightedinbold.
ForExamplech18_11a.sql
Clickheretoviewcodeimage
DECLARE
row_num_tabtest_adm_pkg.row_num_type;
row_text_tabtest_adm_pkg.row_text_type;
v_rowsNUMBER;
BEGIN
—Populatecollections
FORiIN1..10
LOOP
row_num_tab(i):=i;

row_text_tab(i):=‘row‘||i;
ENDLOOP;
—DeletepreviouslyaddeddatafromtheTESTtable
test_adm_pkg.delete_test(row_num_tab);
—PopulateTESTtable
test_adm_pkg.populate_test(row_num_tab,row_text_tab);
COMMIT;
—CheckhowmanyrowswhereinsertedintheTESTtable
—anddisplaythisnumberonthescreen
SELECTCOUNT(*)
INTOv_rows
FROMTEST;
DBMS_OUTPUT.PUT_LINE(‘Thereare‘||v_rows||’rowsintheTESTtable’);
END;

Thisexampleisverysimilartotheexamplech18_1a.sqlusedinLab18.1.Itpopulates
theTESTtable,checkshowmanyrecordswereaddedtotheTESTtable,anddisplaysthis
informationonthescreen.ThemaindifferenceisthatitreferencestheTEST_ADM_PKG
packagewhendeclaringtwocollectionvariablesanditcallstheDELETE_TESTand
POPULATE_TESTprocedurestodeletepreviouslyaddedrecordstotheTESTtableand
repopulatethetablewiththenewdata.Notethatallofthereferencestothepackaged
objectsareprefixedbythepackagename.
Whenrun,thisexampleproducesthefollowingoutput:
Clickheretoviewcodeimage
Thereare10rowsintheTESTtable

Nowconsideramodifiedversionofthisexamplewherecallstotheprocedures
DELETE_TESTandPOPULATE_TESTareembeddedinthedynamicSQL.Allchanges
areshowninbold.
ForExamplech18_11b.sql
Clickheretoviewcodeimage
DECLARE
row_num_tabtest_adm_pkg.row_num_type;
row_text_tabtest_adm_pkg.row_text_type;
v_dyn_sqlVARCHAR2(1000);
v_rowsNUMBER;
BEGIN
—Populatecollections
FORiIN1..10
LOOP
row_num_tab(i):=i;
row_text_tab(i):=‘row‘||i;
ENDLOOP;
—DeletepreviouslyaddeddatafromtheTESTtable
v_dyn_sql:=‘begintest_adm_pkg.delete_test(:row_num_tab);end;’;
EXECUTEIMMEDIATEv_dyn_sqlUSINGrow_num_tab;

—PopulateTESTtable
v_dyn_sql:=‘begintest_adm_pkg.populate_test(:row_num_tab,
:row_text_tab);end;’;
EXECUTEIMMEDIATEv_dyn_sqlUSINGrow_num_tab,row_text_tab;
COMMIT;
—CheckhowmanyrowswhereinsertedintheTESTtable
—displayitonthescreen
SELECTCOUNT(*)
INTOv_rows
FROMTEST;
DBMS_OUTPUT.PUT_LINE(‘Thereare‘||v_rows||’rowsintheTESTtable’);
END;

Thisversionofthescriptdeclaresanewvariable,v_dyn_sql,thatisusedtostore
dynamicSQLstatement.Next,thecallstotheDELETE_TESTandPOPULATE_TEST
proceduresarereplacedbythedynamicSQLstatementsthatareexecutedbythe
EXECUTEIMMEDIATEstatement.
NoticethesyntaxofthedynamicSQLstatements.Intheoriginalexample,the
packagedproceduresareinvokedinthisway:
Clickheretoviewcodeimage
—DeletepreviouslyaddeddatafromtheTESTtable
test_adm_pkg.delete_test(row_num_tab);
—PopulateTESTtable
test_adm_pkg.populate_test(row_num_tab,row_text_tab);

Inthemodifiedversionoftheexample,callstotheseproceduresareplacedbetweenthe
BEGINandENDstatements:
Clickheretoviewcodeimage
—DeletepreviouslyaddeddatafromtheTESTtable
v_dyn_sql:=‘begintest_adm_pkg.delete_test(:row_num_tab);end;’;
EXECUTEIMMEDIATEv_dyn_sqlUSINGrow_num_tab;
—PopulateTESTtable
v_dyn_sql:=‘begintest_adm_pkg.populate_test(:row_num_tab,
:row_text_tab);end;’;
EXECUTEIMMEDIATEv_dyn_sqlUSINGrow_num_tab,row_text_tab;

ThisapproachisusedbecauseeachdynamicSQLstatementisexecutedasananonymous
PL/SQLblockand,therefore,requiresBEGINandENDstatements.IftheseBEGINand
ENDstatementsareomittedfromthedynamicSQL,thescriptwillnotexecute
successfully.Thisisillustratedbythefollowingexample(affectedstatementsareshown
inbold):
ForExamplech18_11c.sql
Clickheretoviewcodeimage
DECLARE
row_num_tabtest_adm_pkg.row_num_type;
row_text_tabtest_adm_pkg.row_text_type;

v_dyn_sqlVARCHAR2(1000);
v_rowsNUMBER;
BEGIN
—Populatecollections
FORiIN1..10
LOOP
row_num_tab(i):=i;
row_text_tab(i):=‘row‘||i;
ENDLOOP;
—DeletepreviouslyaddeddatafromtheTESTtable
v_dyn_sql:=‘test_adm_pkg.delete_test(:row_num_tab);’;
EXECUTEIMMEDIATEv_dyn_sqlUSINGrow_num_tab;
—PopulateTESTtable
v_dyn_sql:=‘test_adm_pkg.populate_test(:row_num_tab,
:row_text_tab);’;
EXECUTEIMMEDIATEv_dyn_sqlUSINGrow_num_tab,row_text_tab;
COMMIT;
—CheckhowmanyrowswhereinsertedintheTESTtable
—displayitonthescreen
SELECTCOUNT(*)
INTOv_rows
FROMTEST;
DBMS_OUTPUT.PUT_LINE(‘Thereare‘||v_rows||’rowsintheTESTtable’);
END;

Thisversionofthescriptproducesthefollowingerror:
Clickheretoviewcodeimage
ORA-00900:invalidSQLstatement
ORA-06512:atline18

Asmentionedpreviously,startingwithOracle12cyoucanalsousebindvariables
basedontherecordtypes.Similartothecollectiontypes,therecordtypesmustbedefined
inthepackagespecification.ConsiderthemodifiedversionofTEST_ADM_PKGshownin
Listing18.4.Thepackagenowcontainsadefinitionofarecordtypeandanewprocedure
thatpopulatesarecordvariablefromtheTESTtableusingthenewlycreatedrecordtype.
Newlyaddeditemsareshowninbold.
Listing18.4TEST_ADM_PKGPackagewithRecordType
Clickheretoviewcodeimage
CREATEORREPLACEPACKAGEtest_adm_pkg
AS
—Definecollectiontypes
TYPErow_num_typeISTABLEOFNUMBERINDEXBYPLS_INTEGER;
TYPErow_text_typeISTABLEOFVARCHAR2(10)INDEXBYPLS_INTEGER;
—Definerecordtype
TYPErec_typeISRECORD
(row_numNUMBER
,row_textVARCHAR2(10));
—Defineprocedures

PROCEDUREpopulate_test(row_num_tabROW_NUM_TYPE
,row_num_typeROW_TEXT_TYPE);
PROCEDUREupdate_test(row_num_tabROW_NUM_TYPE
,row_num_typeROW_TEXT_TYPE);
PROCEDUREdelete_test(row_num_tabROW_NUM_TYPE);
PROCEDUREpopulate_test_rec(row_num_valINNUMBER
,test_recOUTREC_TYPE);
ENDtest_adm_pkg;
/
CREATEORREPLACEPACKAGEBODYtest_adm_pkg
AS
PROCEDUREpopulate_test(row_num_tabROW_NUM_TYPE
,row_num_typeROW_TEXT_TYPE);
IS
BEGIN
FORALLiIN1..10
INSERTINTOtest(row_num,row_text)
VALUES(row_num_tab(i),row_num_type(i));
ENDpopulate_test;
PROCEDUREupdate_test(row_num_tabROW_NUM_TYPE
,row_num_typeROW_TEXT_TYPE);
IS
BEGIN
FORALLiIN1..10
UPDATEtest
SETrow_text=row_num_type(i)
WHERErow_num=row_num_tab(i);
ENDupdate_test;
PROCEDUREdelete_test(row_num_tabROW_NUM_TYPE)
IS
BEGIN
FORALLiIN1..10
DELETEfromtest
WHERErow_num=row_num_tab(i);
ENDdelete_test;
PROCEDUREpopulate_test_rec(row_num_valINNUMBER
,test_recOUTREC_TYPE)
IS
BEGIN
SELECT*
INTOtest_rec
FROMtest
WHERErow_num=row_num_val;
ENDpopulate_test_rec;
ENDtest_adm_pkg;
/

Thisversionofthepackagecontainsdefinitionsoftheuser-definedrecordtype,
rec_type,andanewprocedure,POPULATE_TEST_REC,thatselectsarecordfrom
theTESTtableintothetest_recparameterbasedontherow_numvalueprovidedat
runtime.NotetheINandOUTparametermodesspecifiedinthe

POPULATE_TEST_RECprocedureheader:Theydenotethattherow_num_val
parameterisusedtopassavalueintotheprocedure,andthatthetest_recparameteris
usedtopassavaluefromtheprocedure.(Parametermodesarecoveredindetailin
Chapters19through21.)
Thenextexampleusesthenewlycreatedrecordtypeandproceduretodisplayarecord
fromtheTESTtable.
ForExamplech18_12a.sql
Clickheretoviewcodeimage
DECLARE
test_rectest_adm_pkg.rec_type;
v_dyn_sqlVARCHAR2(1000);
BEGIN
—SelectrecordfromtheTESTtable
v_dyn_sql:=‘begintest_adm_pkg.populate_test_rec(:val,:rec);end;’;
EXECUTEIMMEDIATEv_dyn_sqlUSINGIN10,OUTtest_rec;
COMMIT;
—Displaynewlyselectedrecord
DBMS_OUTPUT.PUT_LINE(‘test_rec.row_num=‘||test_rec.row_num);
DBMS_OUTPUT.PUT_LINE(‘test_rec.row_text=‘||test_rec.row_text);
END;

Inthisexample,theUSINGclauseintheEXECUTEIMMEDIATEstatementcontains
parametermodes.ThisisdonetoensurethatthevariablesusedbytheEXECUTE
IMMEDIATEstatementhavethesamemodesastheparametersintheprocedure.When
run,thisexampleproducesthefollowingoutput:
Clickheretoviewcodeimage
test_rec.row_num=10
test_rec.row_text=row10

BindingCollectionswithOPEN-FOR,FETCH,andCLOSE
Statements
RecallthattheOPEN-FOR,FETCH,andCLOSEstatementsareusedwithmultirowqueries
orcursors.Thisisillustratedbytheexamplebelow.
ForExamplech18_13a.sql
Clickheretoviewcodeimage
DECLARE
TYPEstudent_cur_typISREFCURSOR;
student_curstudent_cur_typ;
student_recstudent%ROWTYPE;
v_zip_codestudent.zip%TYPE:=‘06820’;
BEGIN
OPENstudent_cur

FOR‘SELECT*FROMstudentWHEREzip=:my_zip’USINGv_zip_code;
LOOP
FETCHstudent_curINTOstudent_rec;
EXITWHENstudent_cur%NOTFOUND;
—DisplaystudentID,firstandlastnames
DBMS_OUTPUT.PUT_LINE(‘student_rec.student_id=
‘||student_rec.student_id);
DBMS_OUTPUT.PUT_LINE(‘student_rec.first_name=
‘||student_rec.first_name);
DBMS_OUTPUT.PUT_LINE
(‘student_rec.last_name=’||student_rec.last_name);
ENDLOOP;
CLOSEstudent_cur;
END;

Thedeclarationportionofthisscriptspecifiesthecursortype,student_cur_typ,
definedasREFCURSOR,andacursorvariable,student_cur,basedonthistype.
Next,itdefinesarecordvariable,student_rec,basedontheSTUDENTtable.
TheexecutableportionofthescriptassociatestheSELECTstatementwiththe
STUDENTtableforagivenZIPcodewiththestudent_curvariableandopensit.
Next,eachrowreturnedbytheSELECTstatementisfetchedintothestudent_rec
variableandthestudent’sID,firstname,andlastnamearedisplayedonthescreen.Once
alltherecordsreturnedbytheSELECTstatementarefetched,thecursorterminates.
Whenrun,thisexampleproducesthefollowingoutput:
Clickheretoviewcodeimage
student_rec.student_id=240
student_rec.first_name=Z.A.
student_rec.last_name=Scrittorale
student_rec.student_id=326
student_rec.first_name=Piotr
student_rec.last_name=Padel
student_rec.student_id=360
student_rec.first_name=Calvin
student_rec.last_name=Kiraly

Next,consideramodifiedversionofthisexamplewhereallstudentrecordsforagiven
ZIPcodearefetchedatonceintoacollectionofrecords.Recallthattobindacollectionor
arecordtype,thefollowingrestrictionmustberespected:Thecollectionorrecorddata
typemustbedeclaredinthepackagespecification.Tocomplywiththisrule,the
STUDENT_ADM_PKGpackageshowninListing18.5iscreatedspecificallyforthis
purpose.
Listing18.5STUDENT_ADM_PKGPackagewithRecordandCollectionTypes
Clickheretoviewcodeimage
CREATEORREPLACEPACKAGEstudent_adm_pkg
AS
—Definecollectiontype
TYPEstudent_tab_typeISTABLEOFstudent%ROWTYPEINDEXBYPLS_INTEGER;
—Defineprocedures

PROCEDUREpopulate_student_tab(zip_codeINVARCHAR2
,student_tabOUTstudent_tab_type);
PROCEDUREdisplay_student_info(student_recstudent%ROWTYPE);
ENDstudent_adm_pkg;
/
CREATEORREPLACEPACKAGEBODYstudent_adm_pkg
AS
PROCEDUREpopulate_student_tab(zip_codeINVARCHAR2
,student_tabOUTstudent_tab_type)
IS
BEGIN
SELECT*
BULKCOLLECTINTOstudent_tab
FROMstudent
WHEREzip=zip_code;
ENDpopulate_student_tab;
PROCEDUREdisplay_student_info(student_recstudent%ROWTYPE)
IS
BEGIN
DBMS_OUTPUT.PUT_LINE(‘student_rec.zip=’||student_rec.zip);
DBMS_OUTPUT.PUT_LINE(‘student_rec.student_id=
‘||student_rec.student_id);
DBMS_OUTPUT.PUT_LINE(‘student_rec.first_name=
‘||student_rec.first_name);
DBMS_OUTPUT.PUT_LINE(‘student_rec.last_name=
‘||student_rec.last_name);
ENDdisplay_student_info;
ENDstudent_adm_pkg;
/

Thepackagespecificationdeclarestheassociativearraytype,student_tab_type,
whereeachelementofthecollectionisarecordofthestudent%ROWTYPE.Next,it
declaresthepopulate_student_tabprocedure,whichacceptsthevalueofaZIP
codeandreturnsacollectionofrecords,student_tab,populatedviatheBULK
COLLECTINTOstatement.Notetheparametermodesspecifiedintheprocedure
declaration.Thezip_codeisspecifiedasanINparameter.Basedonitsvalue,theOUT
parameterstudent_tabispopulatedviatheSELECTstatementfromtheSTUDENT
table.
Thesecondprocedure,display_student_info,acceptsoneinputparameter,
student_recbasedontheSTUDENTtableanddisplaysthestudent’sZIPcode,ID,
firstname,andlastnameonthescreen.
Thepackagebodycontainstheexecutablecodeofthepopulate_student_tab
anddisplay_student_infoprocedures.
Thefollowingmodifiedversionofexamplech18_13a.sqlemploysthisnewlycreated
package.Referencestopackagesobjectsarehighlightedinbold.
ForExamplech18_13b.sql

Clickheretoviewcodeimage
DECLARE
TYPEstudent_cur_typISREFCURSOR;
student_curstudent_cur_typ;
—Collectionandrecordvariables
student_tabstudent_adm_pkg.student_tab_type;
student_recstudent%ROWTYPE;
BEGIN
—Populatecollectionofrecords
student_adm_pkg.populate_student_tab(‘06820’,student_tab);
OPENstudent_cur
FOR‘SELECT*FROMTABLE(:my_table)’USINGstudent_tab;
LOOP
FETCHstudent_curINTOstudent_rec;
EXITWHENstudent_cur%NOTFOUND;
student_adm_pkg.display_student_info(student_rec);
ENDLOOP;
CLOSEstudent_cur;
END;

Thisversionofthescriptdeclaresacollectionofrecords,student_tab,basedonthe
collectiontypedefinedintheSTUDENT_ADM_PKGpackage.Theexecutionsectionofthe
scriptpopulatesthestudent_tabcollectionwiththerecordsfromtheSTUDENTtable
foraparticularZIPcode.Thisisaccomplishedbycallingthe
populate_student_tabproceduredefinedintheSTUDENT_ADM_PKGpackage.
Next,thestudentrecordsareselectedfromthenewlypopulatedstudent_tab
collection.Notetheusageofthebuilt-inTABLEfunctionintheSELECTstatement.
DidYouKnow?
TheTABLEfunctionallowsyoutoqueryacollectionlikeaphysicaldatabase
table.Essentially,itacceptsacollectionasitsinputparameterandreturnsthe
appropriateresultsetbasedontheSELECTstatement.Notethataninput
parametercanalsobeaREFCURSOR.
Whenrun,thisversionofthescriptproducesthefollowingoutput:
Clickheretoviewcodeimage
student_rec.zip=06820
student_rec.student_id=240
student_rec.first_name=Z.A.
student_rec.last_name=Scrittorale
student_rec.zip=06820
student_rec.student_id=326
student_rec.first_name=Piotr
student_rec.last_name=Padel
student_rec.zip=06820
student_rec.student_id=360
student_rec.first_name=Calvin
student_rec.last_name=Kiraly

Summary
Inthischapter,youlearnedhowtooptimizePL/SQLcodewithfeaturesknownasbulk
SQL.Fundamentally,youdiscoveredhowtobatchSQLstatementsandtheirresultssoas
tominimizetheperformanceoverheadassociatedwiththenumberofcontextswitches
betweenthePL/SQLandSQLengines.Specifically,youlearnedabouttheFORALL
statementandtheBULKCOLLECTclause.Inaddition,youlearnedthatstartingwith
Oracle12c,youcanemploybulkSQLandcollectiondatatypesalongwithdynamicSQL.
BytheWay
Thecompanionwebsiteprovidesadditionalexercisesandsuggestedanswers
forthischapter,withdiscussionrelatedtohowthoseanswersresulted.The
mainpurposeoftheseexercisesistohelpyoutestthedepthofyour
understandingbyutilizingalloftheskillsthatyouhaveacquiredthroughout
thischapter.

19.Procedures
Inthischapter,youwilllearnabout
CreatingProcedures
PassingParametersINandOUTofProcedures
AllthePL/SQLthatyouhavewrittenuptothispointhasbeenanonymousblocksthat
wererunasscriptsandcompiledbythedatabaseserveratruntime.Nowyouwillbeginto
usemodularcode.Modularcodeisamethodologytobuildaprogramfromdistinctparts
(modules),eachofwhichperformsaspecificfunctionortasktowardthefinalobjectiveof
theprogram.Oncemodularcodeisstoredonthedatabaseserver,itbecomesadatabase
object,orsubprogram,thatisavailabletootherprogramunitsforrepeatedexecution.To
savecodeintothedatabase,thesourcecodeneedstobesenttotheserversothatitcanbe
compiledintop-codeandstoredinthedatabase.Thisprocesswillbecoveredinthe
followingthreechapters.Thischapterisshort:Itsimplyintroducesstoredprocedures.
Chapter20coversthebasicsofstoredfunctions,andChapter21isalengthychapterthat
pullsallthematerialtogethertocoverpackages.
Inthefirstlabofthischapter,youwilllearnmoreaboutstoredcodeanddiscoverhow
towriteonetypeofstoredcodeknownasprocedures.Inthesecondlab,youwilllearn
aboutpassingparametersintoandoutofprocedures.Priortocoveringthedetailsofstored
procedures,youwillbeintroducedtothebenefitsofmodulecode.

BenefitsofModularCode
APL/SQLmoduleisanycompletelogicalunitofwork.TherearefivetypesofPL/SQL
modules:(1)anonymousblocksthatarerunwithatextscript(thetypeyouhaveused
untilnow),(2)procedures,(3)functions,(4)packages,and(5)triggers.Therearetwo
mainbenefitstousingmodularcode:(1)Itismorereusableand(2)itismoremanageable.
YoucreateaprocedureeitherinSQL*Plusorinoneofthemanytoolsforcreatingand
debuggingstoredPL/SQLcode.IfyouareusingSQL*Plus,youwillneedtowriteyour
codeinatexteditorandthenrunitattheSQL*Plusprompt.

BlockStructure
Thesameblockstructureisusedforallthemoduletypes.Theblockbeginswithaheader
(fornamedblocksonly),whichconsistsof(1)thenameofthemoduleand(2)aparameter
list(ifused).
Thedeclarationsectiondefinesvariables,cursors,andsub-blocksthatwillbeneededin
thenextsection.
Themainpartofthemoduleistheexecutionsection,whereallofthecalculationsand
processingareperformed.ThiswillcontainexecutablecodesuchasIF-THEN-ELSE
statements,loops,callstootherPL/SQLmodules,andsoon.