Tải bản đầy đủ
Lab 12.3: FOR UPDATE and WHERE CURRENT Cursors

Lab 12.3: FOR UPDATE and WHERE CURRENT Cursors

Tải bản đầy đủ

ENDIF;
ENDLOOP;
END;

Thisexampleshowshowtoupdatethecostofallcourseswithacostoflessthan2500.It
willincrementeachofthesecostsby10.
SeveralissuesmustbetakenintoaccountwithFORUPDATEcursorsintermsofwhere
toplaceaCOMMITstatement.Thefollowingexampledemonstratesonewayofhandling
thisissue.
ForExamplech12_4.sql
Clickheretoviewcodeimage
DECLARE
CURSORc_grade(
i_student_idINenrollment.student_id%TYPE,
i_section_idINenrollment.section_id%TYPE)
IS
SELECTfinal_grade
FROMenrollment
WHEREstudent_id=i_student_id
ANDsection_id=i_section_id
FORUPDATE;
CURSORc_enrollmentIS
SELECTe.student_id,e.section_id
FROMenrollmente,sections
WHEREs.course_no=135
ANDe.section_id=s.section_id;
BEGIN
FORr_enrollINc_enrollment
LOOP
FORr_gradeINc_grade(r_enroll.student_id,
r_enroll.section_id)
LOOP
UPDATEenrollment
SETfinal_grade=90
WHEREstudent_id=r_enroll.student_id
ANDsection_id=r_enroll.section_id;
ENDLOOP;
ENDLOOP;
END;

PlacingaCOMMITstatementaftereachupdatecanbecostly.Iftherearealotof
updatesandtheCOMMITcomesaftertheblockloop,however,thereisariskofarollback
segmentnotbeinglargeenough.Normally,theCOMMITstatementwouldgoafterthe
loop,exceptwhenthetransactioncountishigh;insuchasituation,youmightwantto
codesomethingthatdoesaCOMMITforeach10,000records.Ifthisscriptwerepartofa
largeprocedure,youmightwanttoputaSAVEPOINTaftertheloop.Then,ifyouneedto
rollbackthisupdateatalaterpoint,itwouldbeaneasytask.
Ifthisexamplewererun,thefinal_gradeforallstudentsenrolledincourse135
wouldbeupdatedto90.Therearetwocursorshere.Onecursorcapturesthestudentswho
areenrolledincourse135andplacesthemintotheactiveset.Theothercursortakesthe
student_idandthesection_idfromthisactiveset,selectsthecorresponding
final_gradefromtheenrollmenttable,andlockstheentireenrollmenttable.The

enrollmentcursorloopisbegunfirst,anditpassesthestudent_idandthe
section_idasINparameterstothesecondcursorloopofthec_gradecursor,which
performstheupdate.ACOMMITstatementshouldbeaddedimmediatelyaftertheupdate
toensurethateachupdateiscommittedtothedatabase.

FORUPDATEOFinaCursor
FORUPDATEOFcanbeusedwhencreatingacursorforanupdateoperationthatis
basedonmultipletables.FORUPDATEOFlockstherowsofatablethatbothcontain
oneofthespecifiedcolumnsandaremembersoftheactiveset.Inotherwords,itisthe
meansofspecifyingwhichtableyouwanttolock.IftheFORUPDATEOFclauseis
used,thenrowsmaynotbefetchedfromthecursoruntilaCOMMIThasbeenissued.
ForExamplech12_5.sql
Clickheretoviewcodeimage
DECLARE
CURSORc_stud_zipIS
SELECTs.student_id,z.city
FROMstudents,zipcodez
WHEREz.city=‘Brooklyn’
ANDs.zip=z.zip
FORUPDATEOFphone;
BEGIN
FORr_stud_zipINc_stud_zip
LOOP
UPDATEstudent
SETphone=‘718’||SUBSTR(phone,4)
WHEREstudent_id=r_stud_zip.student_id;
ENDLOOP;
END;

ThisexampleupdatesthephonenumbersofstudentslivinginBrooklynbychanging
theareacodeto718.Thecursordeclarationlocksthephonecolumnofthestudenttable.
Thislockisneverreleased,however,becausethereisnoCOMMITorROLLBACK
statement.

WHERECURRENTOFinaCursor
UseWHERECURRENTOFwhenyouwanttoupdatethemostrecentlyfetchedrow.This
clausecanonlybeusedwithaFORUPDATEOFcursor.TheadvantageoftheWHERE
CURRENTOFclauseisthatitenablesyoutoeliminatetheWHEREclauseintheUPDATE
statement.
ForExamplech12_6.sql
Clickheretoviewcodeimage
DECLARE
CURSORc_stud_zipIS
SELECTs.student_id,z.city
FROMstudents,zipcodez
WHEREz.city=‘Brooklyn’
ANDs.zip=z.zip

FORUPDATEOFphone;
BEGIN
FORr_stud_zipINc_stud_zip
LOOP
DBMS_OUTPUT.PUT_LINE(r_stud_zip.student_id);
UPDATEstudent
SETphone=‘718’||SUBSTR(phone,4)
WHERECURRENTOFc_stud_zip;
ENDLOOP;
END;

Theselasttwoexamplesperformthesameupdate.TheWHERECURRENTOFclause
allowsyoutoeliminateamatchintheUPDATEstatement,becausetheupdateisbeing
performedforthecurrentrecordofthecursoronly.
DidYouKnow?
TheFORUPDATEandWHERECURRENTOFsyntaxcanbeusedwith
cursorsthatareperformingadeleteaswellasanupdate.

Summary
Thechapterexploredvariousadvancedtopicsinvolvingcursors.First,youlearnedhowto
passparameterstocursorstorestricttheresultsetofacursor.Then,youlearnedhowto
nestcursors.Finally,yousawthesyntaxforcreatingcursorsthatmakedatabaseupdates.
BytheWay
Thecompanionwebsiteprovidesadditionalexercisesandsuggestedanswers
forthischapter,withdiscussionrelatedtohowthoseanswersresulted.The
mainpurposeoftheseexercisesistohelpyoutestthedepthofyour
understandingbyutilizingalloftheskillsthatyouhaveacquiredthroughout
thischapter.

13.Triggers
Inthischapter,youwilllearnabout
WhatTriggersAre
TypesofTriggers
InChapter1,youencounteredtheconceptofnamedPL/SQLblockssuchasprocedures,
functions,andpackagesthatcanbestoredinthedatabase.Inthischapter,youwilllearn
aboutanothertypeofnamedPL/SQLblockcalledadatabasetrigger.Youwillalsolearn
aboutdifferentcharacteristicsoftriggersandtheirusageinthedatabase.

Lab13.1:WhatTriggersAre
Afterthislab,youwillbeableto
DefineaDatabaseTrigger
UseBEFOREandAFTERTriggers
EmployAutonomousTransactions

DatabaseTrigger
AdatabasetriggerisanamedPL/SQLblockthatisstoredinadatabaseandexecuted
implicitlywhenatriggeringeventoccurs.Theactofexecutingatriggerisreferredtoas
firingthetrigger.Atriggeringeventcanbeanyofthefollowing:
ADML(forexample,INSERT,UPDATE,orDELETE)statementexecutedagainsta
databasetable.Suchtriggercanfirebeforeorafteratriggeringevent.Forexample,
ifyouhavedefinedatriggertofirebeforeanINSERTstatementontheSTUDENT
table,thistriggerfireseachtimebeforeyouinsertarowintheSTUDENTtable.
ADDL(forexample,CREATEorALTER)statementexecutedeitherbyaparticular
useragainstaschemaorbyanyuser.Suchtriggersareoftenusedforauditing
purposesandarespecificallyhelpfultoOracledatabaseadministrators.Theycan
recordvariousschemachanges,includingwhenthosechangesweremadeandby
whichuser.
Asystemeventsuchasstartuporshutdownofthedatabase.
Ausereventsuchasloginandlogoff.Forexample,youcandefineatriggerthat
firesafteraloginonadatabaseandthatrecordstheusernameandtimeoflogin.
ThegeneralsyntaxforcreatingatriggerisshowninListing13.1(thereservedwords
andphrasessurroundedbybracketsareoptional).
Listing13.1GeneralSyntaxforCreatingaTrigger

Clickheretoviewcodeimage
CREATE[ORREPLACE][EDITIONABLE|NONEDITIONABLE]TRIGGERtrigger_name
{BEFORE|AFTER}triggering_eventONtable_name
[FOREACHROW]
[FOLLOWS|PRECEDESanother_trigger]
[ENABLE/DISABLE]
[WHENcondition]
DECLARE
Declarationstatements
BEGIN
Executablestatements
EXCEPTION
Exception-handlingstatements
END;

ThereservedwordCREATEspecifiesthatyouarecreatinganewtrigger.Thereserved
wordREPLACEspecifiesthatyouaremodifyinganexistingtrigger.REPLACEis
optional.Note,however,thatbothCREATEandREPLACEareincludedinmostcases.
SupposeyoucreateatriggerasshowninListing13.2.
Listing13.2CreatingTrigger
CREATETRIGGERtrigger_name


Inafewdays,youdecidetomodifythistrigger.Ifyoudonotincludethereservedword
REPLACEintheCREATEclauseofthetrigger,anerrormessagewillbegeneratedwhen
youcompilethetrigger.Theerrormessagestatesthatthenameofyourtriggerisalready
usedbyanotherobject.OnceREPLACEisincludedintheCREATEclauseofthetrigger,
thereislesschanceofanerroroccurringbecauseifitisanewtrigger,itiscreated,andif
itisanoldtrigger,itisreplaced.
However,youshouldbemindfulwhenusingthereservedwordREPLACEforanumber
ofreasons.First,ifyouhappentouseREPLACEwiththenameofanexistingstored
function,procedure,orpackage,youwillendupwithdifferentdatabaseobjectsthathave
thesamename.Thisoccursbecausetriggershaveaseparatenamingspaceinthedatabase.
Whilesharingofthesamenamebyatriggerandaprocedure,function,orpackagedoes
notcauseerrors,potentiallyitmightbecomeconfusing;thusitisnotconsideredagood
programmingpractice.Second,whenyouusethereservedwordREPLACEanddecideto
associateadifferenttablewithyourtrigger,anerrormessageisgenerated.Forexample,
assumeyoucreatedatriggerSTUDENT_BIontheSTUDENTtable.Next,youdecideto
modifythistriggerandassociateitwiththeENROLLMENTtable.Asaresult,the
followingerrormessageisgenerated:
Clickheretoviewcodeimage
ORA-04095:trigger‘STUDENT_BI’alreadyexistsonanothertable,cannot
replaceit

TheoptionalreservedwordsEDITIONABLEandNONEDITIONABLEspecifywhether
atriggerisaneditionedornoneditionedobject.Notethatthisdesignationappliesonlyif
editioninghasbeenenabledforobjecttypeTRIGGER.

DidYouKnow?
Oracleintroducedaveryimportantfeaturecallededition-basedredefinitionin
version11g,release2.Thisfeatureenablesyoutoapplychangestovarious
databaseobjectswithoutinvalidatingthewholesystem,therebyallowingfor
near-zerodowntime.Forexample,previouslymakingstructuralchangestoa
tablewouldinvalidatenumerousfunctions,procedures,andpackages
dependentonthattable.Asaresult,youwouldneedtocheckandrecompile
allinvalidateddatabaseobjects,potentiallyrequiringdowntimeforthe
database.Withedition-basedredefinition,youcanimplementallthese
changesseamlesslyandmigrateusersfromtheoldversionofthesystemto
thenewversionwithoutincurringanydowntime.
Edition-basedredefinitionisoutsidethescopeofthisbook.Detailed
informationonthisfeaturecanbefoundinOracle’sonlinehelp
(www.oracle.com).
Thetrigger_namereferencesthenameofthetrigger.BEFOREorAFTERspecifies
whenthetriggerfires(beforeorafterthetriggeringevent).Thetriggering_event
referencesaDMLstatementissuedagainstthetable.Thetable_nameisthenameof
thetableassociatedwiththetrigger.TheclauseFOREACHROWspecifiesthatatrigger
isarow-leveltriggerandfiresonceforeachroweitherinserted,updated,ordeleted.You
willencounterrow-andstatement-leveltriggersinLab13.2.AWHENclausespecifiesa
conditionthatmustevaluatetoTRUEforthetriggertofire.Forexample,thiscondition
mayspecifyacertainrestrictiononthecolumnofatable.
Thenexttwooptions,FOLLOWS/PRECEDESandENABLE/DISABLE,wereaddedto
theCREATEORREPLACETRIGGERclauseinOracle11g.PriortoOracle11g,you
neededtoissuetheALTERTRIGGERcommandtoenableordisableatriggeronceithad
beencreated.TheENABLE/DISABLEoptionspecifieswhetheratriggeriscreatedinthe
enabledordisabledstate.Whenatriggerisenabled,itfireswhenatriggeringevent
occurs.Conversely,whenatriggerisdisabled,itdoesnotfirewhenatriggeringevent
occurs.NotethatwhenatriggerisfirstcreatedwithoutENABLE/DISABLEoption,itis
enabledbydefault.Todisablethetrigger,youneedtoissuetheALTERTRIGGER
command,asshowninListing13.3.
Listing13.3DisablingTrigger
Clickheretoviewcodeimage
ALTERTRIGGERtrigger_nameDISABLE;

Similarly,toenableatriggerthatwasdisabledpreviously,youissuetheALTER
TRIGGERcommand,asshowninListing13.4.
Listing13.4EnablingTrigger
Clickheretoviewcodeimage
ALTERTRIGGERtrigger_nameENABLE;

TheFOLLOWS/PRECEDESoptionallowsyoutospecifytheorderinwhichtriggers

shouldfire.Itappliestotriggersthataredefinedonthesametableandfireatthesame
timingpoint.Forexample,ifyoudefinedtwotriggersontheSTUDENTtablethatfire
beforetheinsertoperationiscarriedout,Oracledoesnotguaranteetheorderinwhich
thesetriggerswillfireunlessyouexplicitlyspecifyitwiththe
FOLLOWS/PRECEDESclause.Notethatthetriggerreferencedinthe
FOLLOWS/PRECEDESclausemustalreadyexistandhavebeensuccessfullycompiled.
Theportionofthetriggerdescribedtothispointisoftenreferredtoasthetrigger
header.Next,wedefinethetriggerbody.Thebodyofatriggerhasthesamestructureas
ananonymousPL/SQLblock.SimilartothecaseforaPL/SQLblock,thedeclarationand
exceptionsectionsareoptional.
Triggersareusedfordifferentpurposes,suchasthefollowing:
Enforcingcomplexbusinessrulesthatcannotbedefinedbyusingintegrity
constraints
Maintainingcomplexsecurityrules
Automaticallygeneratingvaluesforderivedcolumns
Collectingstatisticalinformationontableaccesses
Preventinginvalidtransactions
Providingvalueauditing
ThebodyofatriggerisaPL/SQLblock.However,severalrestrictionsapplywhenyou
decidetocreateatrigger:
AtriggermaynotissueatransactionalcontrolstatementsuchasCOMMIT,
SAVEPOINT,orROLLBACK.Whenthetriggerfires,alloperationsperformedby
thetriggerbecomepartofatransaction.Whenatransactioniscommittedorrolled
back,theoperationsperformedbythetriggerarecommittedorrolledbackaswell.
Anexceptiontothisruleisatriggerthatcontainsanautonomoustransaction.
Autonomoustransactionsarediscussedindetaillaterinthislab.
Anyfunctionorprocedurecalledbyatriggermaynotissueatransactionalcontrol
statementunlessitcontainsanautonomoustransaction.
ItisnotpermissibletodeclareLONGorLONGRAWvariablesinthebodyofa
trigger.
DidYouKnow?
Ifyoudropatable,thetable’sdatabasetriggersaredroppedaswell.

BEFORETriggers
ConsiderthefollowingexampleofatriggerontheSTUDENTtablementionedearlierin
thischapter.ThistriggerfiresbeforetheINSERTstatementontheSTUDENTtableand
populatestheSTUDENT_ID,CREATED_DATE,MODIFIED_DATE,CREATED_BY,and
MODIFIED_BYcolumns.ThecolumnSTUDENT_IDispopulatedwiththenumber
generatedbytheSTUDENT_ID_SEQsequence,andthecolumnsCREATED_DATE,
MODIFIED_DATE,CREATED_USER,andMODIFIED_USERarepopulatedwiththe
currentdateandthecurrentusernameinformation,respectively.
ForExamplech13_1a.sql
Clickheretoviewcodeimage
CREATEORREPLACETRIGGERstudent_bi
BEFOREINSERTONSTUDENT
FOREACHROW
BEGIN
:NEW.student_id:=STUDENT_ID_SEQ.NEXTVAL;
:NEW.created_by:=USER;
:NEW.created_date:=SYSDATE;
:NEW.modified_by:=USER;
:NEW.modified_date:=SYSDATE;
END;

ThistriggerfiresforeachrowbeforetheINSERTstatementontheSTUDENTtable.
NoticethatthenameofthetriggerisSTUDENT_BI,where“STUDENT”referencesthe
nameofthetableonwhichthetriggerisdefinedandtheletters“BI”mean“beforeinsert.”
Thereisnospecificrequirementfornamingtriggers;however,thisapproachtonaminga
triggerisdescriptivebecausethenameofthetriggercontainsthenameofthetable
affectedbythetriggeringevent,thetimeofthetriggeringevent(beforeorafter),andthe
triggeringeventitself.
Inthebodyofthetrigger,thereisapseudorecord,:NEW,whichallowsforaccessinga
rowthatiscurrentlybeingprocessed.Inotherwords,arowisinsertedintotheSTUDENT
table.The:NEWpseudorecordisofatypeTRIGGERING_TABLE%TYPE,so,inthis
case,itisoftheSTUDENT%TYPEtype.Toaccessindividualmembersofthe
pseudorecord:NEW,dotnotationisused.Inotherwords,:NEW.CREATED_BYrefersto
thememberCREATED_BYofthe:NEWpseudorecord,andthenameoftherecordis
separatedbythedotfromthenameofitsmember.

DidYouKnow?
Inadditiontothe:NEWpseudorecord,an:OLDpseudorecordexists.It
allowsyoutoaccessthecurrentinformationoftherecordthatisbeing
updatedordeleted.Thusthe:OLDpseudorecordisundefinedforthe
INSERTstatementsandthe:NEWpseudorecordisundefinedforthe
DELETEstatements.However,thePL/SQLcompilerdoesnotgenerate
syntaxerrorswhen:OLDor:NEWpseudorecordsareusedintriggerswhere
thetriggeringeventisanINSERTorDELETEoperation,respectively.Inthis
case,themembervaluesaresettoNULLforthe:OLDand:NEW
pseudorecords.
TakeacloserlookatthestatementthatassignsasequencevaluetotheSTUDENT_ID
column.TheabilitytoaccessasequenceviaPL/SQLexpressionsisanewfeatureadded
inOracle11g.PriortoOracle11g,sequencescouldbeaccessedonlyviaqueries,asshown
inthenextversionoftheexample.
ForExampleCodeFragmentBasedonch13_1a.sql
Clickheretoviewcodeimage
CREATEORREPLACETRIGGERstudent_bi

DECLARE
v_student_idSTUDENT.STUDENT_ID%TYPE;
BEGIN
SELECTSTUDENT_ID_SEQ.NEXTVAL
INTOv_student_id
FROMdual;

END;

TocreatethistriggerontheSTUDENTtableinSQLDeveloper,youmaychoosefrom
thetwooptions.First,thetriggercanbecreatedbyexecutingthescriptintheWorksheet
window,justasyouwouldwithanyotherPL/SQLblock.Atthetimeoftrigger
compilation,youarepromptedtoenterthevalueforbindvariablesbecauseofthe
referencestothe:NEWand:OLDpseudorecordsinthebodyofthetrigger,asshownin
Figure13.1.NotethatcheckboxnexttoNULL.Ifitischecked,simplyclicktheApply
buttonandthetriggerwillbecreated.Ifthischeckboxisnotchecked,thencheckitand
clicktheApplybutton.

Figure13.1CreatingaDatabaseTriggerintheWorksheetWindow
Thesecondoptionforcreatingatriggeristoright-clickonTriggersandchoosethe
NewTriggeroption,asshowninFigure13.2.ThisactivatestheCreateTriggerwindow,as
showninFigure13.3.Inthiswindow,youprovideschemaname,triggername,table
name,thetimingofthetriggeringevent,andtheeventonwhichthetriggershouldfire.

Figure13.2CreatingaDatabaseTriggerviaNewTriggerOption

Figure13.3CreateTriggerWindow
NotethattheschemanamehasalreadybeensettoSTUDENT,andadefaultnamefor
thetriggerhasbeenprovided,TRIGGER1,thatshouldbechangedtoSTUDENT_BI.In
addition,theBaseTypehasbeensettoaTABLEandBaseObjectSchemahasbeensetto
STUDENT.ThisimpliesthatatriggerisbeingcreatedonatableintheSTUDENTschema.
Next,theBaseObjectmustbeselectedfromthedrop-downmenu—inthiscase,itis
STUDENTtable.UndertheEventsoption,theINSERToptionismovedfromthe
AvailableEventstoSelectedEvents.Bydefault,theStatementLevelcheckboxis
enabled.Becauseyouarecreatingarow-leveltrigger,thisoptionshouldbeunchecked.
Finally,thereisanoptiontoprovidedifferentnamesforthe:NEWand:OLD
pseudorecordsandoneormoreconditionsfortheWHENclause.Afteryoufillinthe
CreateTriggerwindowfortheSTUDENT_BItrigger,itshouldcontaintheinformation
showninFigure13.4.