Tải bản đầy đủ
Lab 18.2: The BULK COLLECT Clause

Lab 18.2: The BULK COLLECT Clause

Tải bản đầy đủ

ForExamplech18_6a.sql
Clickheretoviewcodeimage
DECLARE
CURSORstudent_curIS
SELECTstudent_id,first_name,last_name
FROMstudent;
BEGIN
FORrecINstudent_cur
LOOP
DBMS_OUTPUT.PUT_LINE(‘student_id:‘||rec.student_id);
DBMS_OUTPUT.PUT_LINE(‘first_name:‘||rec.first_name);
DBMS_OUTPUT.PUT_LINE(‘last_name:’||rec.last_name);
ENDLOOP;
END;

RecallthatthecursorFORloopopensandclosesthecursorandfetchescursorrecords
implicitly.
ThesametaskoffetchingrecordsfromtheSTUDENTtablecanbeaccomplishedby
employingtheBULKCOLLECTclause.ThedifferencehereisthattheBULKCOLLECT
clausewillfetchallrowsfromtheSTUDENTtableatonce.BecauseBULKCOLLECT
fetchesmultiplerows,theserowsarestoredincollectionvariables.
Inthefollowingmodifiedversionoftheprecedingexample,cursorprocessingis
replacedbytheBULKCOLLECTclause.
ForExamplech18_6b.sql
Clickheretoviewcodeimage
DECLARE
—Definecollectiontypeandvariablestobeusedbythe
—BULKCOLLECTclause
TYPEstudent_id_typeISTABLEOFstudent.student_id%TYPE;
TYPEfirst_name_typeISTABLEOFstudent.first_name%TYPE;
TYPElast_name_typeISTABLEOFstudent.last_name%TYPE;
student_id_tabstudent_id_type;
first_name_tabfirst_name_type;
last_name_tablast_name_type;
BEGIN
—FetchallstudentdataatonceviaBULKCOLLECTclause
SELECTstudent_id,first_name,last_name
BULKCOLLECTINTOstudent_id_tab,first_name_tab,last_name_tab
FROMstudent;
FORiINstudent_id_tab.FIRST..student_id_tab.LAST
LOOP
DBMS_OUTPUT.PUT_LINE(‘student_id:‘||student_id_tab(i));
DBMS_OUTPUT.PUT_LINE(‘first_name:‘||first_name_tab(i));
DBMS_OUTPUT.PUT_LINE(‘last_name:’||last_name_tab(i));
ENDLOOP;
END;

Thisscriptdeclaresthreenestedtabletypesandvariables.Thesevariablesareusedto
storedatareturnedbytheSELECTstatementwiththeBULKCOLLECTclause.Because
thisversionofthescriptisusingtheBULKCOLLECTclause,thereisnoneedtodeclare

andprocessacursor.
DidYouKnow?
WhennestedtablesarepopulatedviaSELECTwiththeBULKCOLLECT
clause,theyareinitializedandextendedautomatically.Recallthatanested
tablemustusuallybeinitializedpriortoitsusebycallingaconstructor
functionthathasthesamenameasitsnestedtabletype.Onceithasbeen
initialized,thenestedtablemustbeextendedviatheEXTENDmethodbefore
thenextvaluecanbeassignedtoit.
Todisplaythedatathathasbeenselectedintotheindividualcollections,thescriptloops
throughthemviathenumericFORloop.Noticethatthelowerandupperlimitsforthe
loopcounterarespecifiedviatheFIRSTandLASTmethods.
DidYouKnow?
TheBULKCOLLECTclauseissimilartoacursorloopinthatitdoesnot
raiseaNO_DATA_FOUNDexceptionwhentheSELECTstatementdoesnot
returnanyrecords.Asaresult,itisconsideredgoodpracticetocheck
whethertheresultingcollectioncontainsanydata.
BecausetheBULKCOLLECTclausedoesnotrestrictthesizeofacollectionand
extendsitautomatically,itisalsoagoodideatolimittheresultsetwhenaSELECT
statementreturnslargeamountsofdata.ThiscanbeachievedbyusingBULKCOLLECT
withacursorSELECTstatementandbyaddingtheLIMIToption.
ForExamplech18_6c.sql
Clickheretoviewcodeimage
DECLARE
CURSORstudent_curIS
SELECTstudent_id,first_name,last_name
FROMstudent;
—Definecollectiontypeandvariablestobeusedbythe
—BULKCOLLECTclause
TYPEstudent_id_typeISTABLEOFstudent.student_id%TYPE;
TYPEfirst_name_typeISTABLEOFstudent.first_name%TYPE;
TYPElast_name_typeISTABLEOFstudent.last_name%TYPE;
student_id_tabstudent_id_type;
first_name_tabfirst_name_type;
last_name_tablast_name_type;
—DefinevariabletobeusedbytheLIMITclause
v_limitPLS_INTEGER:=50;
BEGIN
OPENstudent_cur;
LOOP
—Fetch50rowsatonce
FETCHstudent_cur
BULKCOLLECTINTOstudent_id_tab,first_name_tab,last_name_tab

LIMITv_limit;
EXITWHENstudent_id_tab.COUNT=0;
FORiINstudent_id_tab.FIRST..student_id_tab.LAST
LOOP
DBMS_OUTPUT.PUT_LINE(‘student_id:‘||student_id_tab(i));
DBMS_OUTPUT.PUT_LINE(‘first_name:‘||first_name_tab(i));
DBMS_OUTPUT.PUT_LINE(‘last_name:’||last_name_tab(i));
ENDLOOP;
ENDLOOP;
CLOSEstudent_cur;
END;

ThisversionofthescriptemploystheBULKCOLLECTclausewiththeLIMIToption
tofetch50rowsfromtheSTUDENTtableatonce.Inotherwords,eachcollectionwill
containatmost50records.Toaccomplishthistask,theBULKCOLLECTclauseisused
inconjunctionwiththecursorloop.Inthiscase,theexitconditionoftheloopisbasedon
thenumberofrecordsinthecollectionratherthanthestudent_cur%NOTFOUND
attribute.
NotehowthenumericFORloopthatdisplaysinformationonthescreenhasbeen
movedinsidethecursorloop.Thisisdonebecauseeverynewbatchof50recordsfetched
bytheBULKCOLLECTclausewillreplacethepreviousbatchof50recordsfetchedin
thepreviousiteration.
Sofar,youhaveseenexamplesoftheBULKCOLLECTclausefetchingdatainto
collectionswheretheunderlyingelementsaresimpledatatypessuchasNUMBERor
VARCHAR2.However,theBULKCOLLECTclausecanalsobeusedtofetchdatainto
collectionsofrecordsorobjects.CollectionsofobjectsarediscussedinChapter23.Inthe
followingmodifiedversionofthepreviousexample,studentdataisfetchedintoa
collectionofuser-definedrecords.
ForExamplech18_6d.sql
Clickheretoviewcodeimage
DECLARE
CURSORstudent_curIS
SELECTstudent_id,first_name,last_name
FROMstudent;
—Definerecordtype
TYPEstudent_recISRECORD
(student_idstudent.student_id%TYPE,
first_namestudent.first_name%TYPE,
last_namestudent.last_name%TYPE);
—Definecollectiontype
TYPEstudent_typeISTABLEOFstudent_rec;
—Definecollectionvariable
student_tabstudent_type;
—DefinevariabletobeusedbytheLIMITclause
v_limitPLS_INTEGER:=50;

BEGIN
OPENstudent_cur;
LOOP
—Fetch50rowsatonce
FETCHstudent_curBULKCOLLECTINTOstudent_tabLIMITv_limit;
EXITWHENstudent_tab.COUNT=0;
FORiINstudent_tab.FIRST..student_tab.LAST
LOOP
DBMS_OUTPUT.PUT_LINE(‘student_id:‘||student_tab(i).student_id);
DBMS_OUTPUT.PUT_LINE(‘first_name:‘||student_tab(i).first_name);
DBMS_OUTPUT.PUT_LINE(‘last_name:’||student_tab(i).last_name);
ENDLOOP;
ENDLOOP;
CLOSEstudent_cur;
END;

Inthisversionofthescript,theresultsetreturnedbythecursorisfetchedinto
collectionofuser-definedrecords,student_tab.Asaconsequence,theFETCH
statementwiththeBULKCOLLECTIONoptiondoesnotneedtoreferenceindividual
recordelements.
Allversionsofthisexampleproducethesameoutput,aportionofwhichisshownhere:
student_id:230
first_name:George
last_name:Kocka
student_id:232
first_name:Janet
last_name:Jung
student_id:233
first_name:Kathleen
last_name:Mulroy
student_id:234
first_name:Joel
last_name:Brendler


SofaryouhaveseenhowtousetheBULKCOLLECTclausewiththeSELECT
statement.However,oftentimesBULKCOLLECTisusedwithINSERT,UPDATE,and
DELETEstatements.Inthiscase,theBULKCOLLECTclausemaybeusedinconjunction
withtheRETURNINGclause,asshowninthefollowingexample.
ForExamplech18_7a.sql
Clickheretoviewcodeimage
DECLARE
—Definecollectiontypesandvariables
TYPErow_num_typeISTABLEOFNUMBERINDEXBYPLS_INTEGER;
TYPErow_text_typeISTABLEOFVARCHAR2(10)INDEXBYPLS_INTEGER;
row_num_tabrow_num_type;
row_text_tabrow_text_type;
BEGIN
DELETEFROMtest
RETURNINGrow_num,row_text

BULKCOLLECTINTOrow_num_tab,row_text_tab;
DBMS_OUTPUT.PUT_LINE(‘Deleted‘||SQL%ROWCOUNT||’rows:’);
FORiINrow_num_tab.FIRST..row_num_tab.LAST
LOOP
DBMS_OUTPUT.PUT_LINE
(‘row_num=‘||row_num_tab(i)||’row_text=‘||row_text_tab(i));
ENDLOOP;
COMMIT;
END;

ThisscriptdeletesrecordsfromtheTESTtablecreatedandpopulatedinLab18.1.The
DELETEstatementreturnstheROW_NUMandROW_TEXTvaluesviatheRETURNING
clause.ThesevaluesarethenfetchedbytheBULKCOLLECTclauseintotwocollections,
row_num_tabandrow_text_tab.Next,todisplaythedatathathasbeenfetched
intotheindividualcollections,theyareloopedthroughviathenumericFORloop.
Whenrun,thisscriptproducesthefollowingoutput:
Clickheretoviewcodeimage
Deleted7rows:
row_num=2row_text=row2
row_num=3row_text=row3
row_num=4row_text=row4
row_num=6row_text=row6
row_num=8row_text=row8
row_num=9row_text=row9
row_num=10row_text=row10

Asmentionedpreviously,theBULKCOLLECTclauseissimilartothecursorloopin
thatitdoesnotgenerateaNO_DATA_FOUNDexceptionwhennorowsarereturnedbythe
SELECTstatement.Thisisillustratedbythefollowingexample.
ForExamplech18_8a.sql
Clickheretoviewcodeimage
DECLARE
—Definecollectiontypesandvariables
TYPErow_num_typeISTABLEOFNUMBERINDEXBYPLS_INTEGER;
TYPErow_text_typeISTABLEOFVARCHAR2(10)INDEXBYPLS_INTEGER;
row_num_tabrow_num_type;
row_text_tabrow_text_type;
BEGIN
SELECTrow_num,row_text
BULKCOLLECTINTOrow_num_tab,row_text_tab
FROMtest;
FORiINrow_num_tab.FIRST..row_num_tab.LAST
LOOP
DBMS_OUTPUT.PUT_LINE
(‘row_num=‘||row_num_tab(i)||’row_text=‘||row_text_tab(i));
ENDLOOP;
END;

Inthisexample,thedataisselectedfromtheTESTtableandpopulatedintotwo
collections,row_num_tabandrow_text_tab.ThisisaccomplishedviatheBULK
COLLECTclause.Next,thecollectiondataisdisplayedonthescreenthroughthenumeric
FORloop,withthevaluesreturnedbytherow_num_tab.FIRSTand
row_num_tab.LASTmethodsbeingusedaslowerandupperboundsoftheloop.
Atfirstglance,thisexampleseemsverysimilartotheexamplech18_6b.sqlthat
appearedearlierinthislab,asitfollowsthesamesteps.First,collectiontypesand
variablesaredeclared.Second,thedataisselectedinthecollectionvariables.Third,the
datainthecollectionvariablesisdisplayedonthescreen.However,whenitisrun,this
scriptraisesthefollowingexception:
Clickheretoviewcodeimage
ORA-06502:PL/SQL:numericorvalueerror
ORA-06512:atline14

Thiserroriscausedbythe
Clickheretoviewcodeimage
FORiINrow_num_tab.FIRST..row_num_tab.LAST

statement,asthecollectionvariablesdonothaveanydatainthem.Thissituationoccurs
becausethedatafromtheTESTtablewasdeletedinthech18_7a.sqlexample.Toremedy
thisproblem,theexampleshouldbemodifiedasfollows(affectedstatementsareshownin
bold):
ForExamplech18_8b.sql
Clickheretoviewcodeimage
DECLARE
—Definecollectiontypesandvariables
TYPErow_num_typeISTABLEOFNUMBERINDEXBYPLS_INTEGER;
TYPErow_text_typeISTABLEOFVARCHAR2(10)INDEXBYPLS_INTEGER;
row_num_tabrow_num_type;
row_text_tabrow_text_type;
BEGIN
SELECTrow_num,row_text
BULKCOLLECTINTOrow_num_tab,row_text_tab
FROMtest;
IFrow_num_tab.COUNT!=0
THEN
FORiINrow_num_tab.FIRST..row_num_tab.LAST
LOOP
DBMS_OUTPUT.PUT_LINE
(‘row_num=‘||row_num_tab(i)||’row_text=‘||row_text_tab(i));
ENDLOOP;
ELSE
DBMS_OUTPUT.PUT_LINE(‘row_num_tab.COUNT=‘||row_num_tab.COUNT);
DBMS_OUTPUT.PUT_LINE(‘row_text_tab.COUNT=‘||row_text_tab.COUNT);
ENDIF;
END;

Whenrun,thisversionofthescriptdoesnotcauseanyexception.TheIFstatement
evaluatestoFALSEwhentheCOUNTmethodreturns0,asillustratedbytheoutput:

row_num_tab.COUNT=0
row_text_tab.COUNT=0

Throughoutthischapter,youhaveseenhowtousetheFORALLstatementandBULK
COLLECTclause.Nowwewillconsideranexamplethatcombinesbothtechniques.This
exampleusestheMY_ZIPCODEtable,whichiscreatedbasedontheZIPCODEtable.
NotethattheCREATETABLEstatementcreatesanemptytablebecausethecriteria
specifiedintheWHEREclausedonotreturnanyrecords.
ForExamplech18_9a.sql
Clickheretoviewcodeimage
CREATETABLEmy_zipcodeAS
SELECT*
FROMzipcode
WHERE1=2;
DECLARE
—Declarecollectiontypes
TYPEstring_typeISTABLEOFVARCHAR2(100)INDEXBYPLS_INTEGER;
TYPEdate_typeISTABLEOFDATEINDEXBYPLS_INTEGER;
—DeclarecollectionvariablestobeusedbytheFORALLstatement
zip_tabstring_type;
city_tabstring_type;
state_tabstring_type;
cr_by_tabstring_type;
cr_date_tabdate_type;
mod_by_tabstring_type;
mod_date_tabdate_type;
v_rowsINTEGER:=0;
BEGIN
—Populateindividualcollections
SELECT*
BULKCOLLECTINTOzip_tab,city_tab,state_tab,cr_by_tab,
cr_date_tab,mod_by_tab,mod_date_tab
FROMzipcode
WHEREstate=‘CT’;
—PopulateMY_ZIPCODEtable
FORALLiin1..zip_tab.COUNT
INSERTINTOmy_zipcode
(zip,city,state,created_by,created_date,modified_by,
modified_date)
VALUES
(zip_tab(i),city_tab(i),state_tab(i),cr_by_tab(i),
cr_date_tab(i),mod_by_tab(i),mod_date_tab(i));
COMMIT;
—CheckhowmanyrecordswereaddedtoMY_ZIPCODEtable
SELECTCOUNT(*)
INTOv_rows
FROMmy_zipcode;
DBMS_OUTPUT.PUT_LINE(v_rows||’recordswereaddedtoMY_ZIPCODE
table’);
END;

ThisscriptpopulatestheMY_ZIPCODEtablewiththerecordsselectedfromthe
ZIPCODEtable.ToenableuseoftheBULKCOLLECTandFORALLstatements,it
employssevencollections.Notethatthereareonlytwocollectiontypesassociatedwith
thesesevencollections.Thisisbecausetheindividualcollectionsstoreonlytwodata
types,VARCHAR2andDATE.Whenrun,thisexampleproducesthefollowingoutput:
Clickheretoviewcodeimage
19recordswereaddedtoMY_ZIPCODEtable

Next,consideranotherexamplewheretheFORALLstatementandBULKCOLLECT
clauseareusedtogetherwiththeDELETEstatement.Inthisexample,therecordsfromthe
MY_ZIPCODEtablearedeletedforafewZIPcodes,andthecorrespondingcitynames
alongwiththedeletedZIPcodesarestoredinthecity_tabandzip_tabcollections,
respectively.
ForExamplech18_10a.sql
Clickheretoviewcodeimage
DECLARE
—Declarecollectiontypes
TYPEstring_typeISTABLEOFVARCHAR2(100);
—DeclarecollectionvariablestobeusedbytheFORALLstatement
—andBULKCOLLECTclause
zip_codesstring_type:=string_type(‘06401’,‘06455’,‘06483’,
‘06520’,‘06605’);
zip_tabstring_type;
city_tabstring_type;
v_rowsINTEGER:=0;
BEGIN
—DeletesomerecordsfromMY_ZIPCODEtable
FORALLiinzip_codes.FIRST..zip_codes.LAST
DELETEFROMmy_zipcode
WHEREzip=zip_codes(i)
RETURNINGzip,city
BULKCOLLECTINTOzip_tab,city_tab;
COMMIT;
DBMS_OUTPUT.PUT_LINE(‘Thefollowingrecordsweredeletedfrom
MY_ZIPCODEtable:’);
FORiinzip_tab.FIRST..zip_tab.LAST
LOOP
DBMS_OUTPUT.PUT_LINE(‘Zipcode‘||zip_tab(i)||’,city
‘||city_tab(i));
ENDLOOP;
END;

Inthisscript,theFORALLstatementrunstheDELETEstatementforagivenlistofZIP
codevaluesstoredinthezip_codescollection.Also,theDELETEstatementcontains
theRETURNINGclausewiththeBULKCOLLECTclause,whichinturnstoresZIPcodes
andcitynamesinthezip_tabandcity_tabcollections,respectively.Finally,the
numericFORloopisusedtodisplaythedatastoredinthezip_tabandcity_tab
collections,asillustratedbytheoutputfromthescript:

Clickheretoviewcodeimage
ThefollowingrecordsweredeletedfromMY_ZIPCODEtable:
Zipcode06401,cityAnsonia
Zipcode06455,cityMiddlefield
Zipcode06483,cityOxford
Zipcode06520,cityNewHaven
Zipcode06605,cityBridgeport

Lab18.3:BindingCollectionsinSQLStatements
Afterthislab,youwillbeableto
BindCollectionsWhenUsingEXECUTEIMMEDIATEStatements
BindCollectionsWhenUsingOPEN-FOR,FETCH,andCLOSEStatements
Asmentionedpreviously,theabilitytobindcollectiondatatypeswhenemploying
dynamicSQLhasbeenaddedinOracle12c.RecallthatdynamicSQLwascoveredin
Chapter17,whereyoulearnedhowtousetheEXECUTEIMMEDIATEstatementand
OPEN-FOR,FETCH,andCLOSEstatements.

BindingCollectionswithEXECUTEIMMEDIATEStatements
InChapter17,yousawnumerousexamplesoftheEXECUTEIMMEDIATEstatement.
Alloftheseexampleshaveonethingincommon:Thedatatypesofthebindvariablesare
knownSQLtypes.Inotherwords,thesedatatypesareallsupportedbySQL,suchas
NUMBERandVARCHAR2.InOracle12c,itispossibletousebindvariablesbasedonthe
collectionandrecordtypes,albeitwithonerestrictionapplied:Thecollectionorrecord
datatypemustbedeclaredinthepackagespecification.
ConsiderthepackagecalledTEST_ADM_PKG,showninListing18.3.Thispackage
containsdefinitionsoftwocollectiontypesandthreeproceduresthatinsert,update,and
deleterecordsfromtheTESTtable.(Procedures,functions,andpackagesarecoveredin
detailinChapters19through21.)
Listing18.3TEST_ADM_PKGPackagewithCollectionTypes
Clickheretoviewcodeimage
CREATEORREPLACEPACKAGEtest_adm_pkg
AS
—Definecollectiontypes
TYPErow_num_typeISTABLEOFNUMBERINDEXBYPLS_INTEGER;
TYPErow_text_typeISTABLEOFVARCHAR2(10)INDEXBYPLS_INTEGER;
—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);
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;
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;