Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
(//docs.oracle.com/en/)
Home(../../../../)/Database(../../../../en/database/database.html)/OracleDatabaseOnlineDocumentation,10gRelease2(10.2)(../../index.htm)/ApplicationDevelopment(../../nav/portal_4.htm)
DatabaseApplicationDeveloper'sGuideFundamentals
()
()()
Categories
Home(../../index.htm)
Download
9()CodingTriggers
Triggersareproceduresthatarestoredinthedatabaseandareimplicitlyrun,or()()fired,whensomethinghappens.
Traditionally,triggerssupportedtheexecutionofaPL/SQLblockwhenan INSERT, UPDATE,or DELETEoccurredonatableorview.
Triggerssupportsystemandotherdataeventson DATABASEand SCHEMA.OracleDatabasealsosupportstheexecutionofPL/SQL
orJavaprocedures.
ThischapterdiscussesDMLtriggers, INSTEAD OFtriggers,andsystemtriggers(triggerson DATABASEand SCHEMA).Topics
include:
DesigningTriggers
CreatingTriggers
CodingtheTriggerBody
CompilingTriggers
ModifyingTriggers
EnablingandDisablingTriggers
ViewingInformationAboutTriggers
ExamplesofTriggerApplications
RespondingtoSystemEventsthroughTriggers
()
()
DesigningTriggers()
Usethefollowingguidelineswhendesigningyourtriggers:
Usetriggerstoguaranteethatwhenaspecificoperationisperformed,relatedactionsareperformed.
DonotdefinetriggersthatduplicatefeaturesalreadybuiltintoOracleDatabase.Forexample,donotdefinetriggerstorejectbad
dataifyoucandothesamecheckingthroughdeclarativeintegrityconstraints.()()
Limitthesizeoftriggers.Ifthelogicforyourtriggerrequiresmuchmorethan60linesofPL/SQLcode,itisbettertoincludemost
ofthecodeinastoredprocedureandcalltheprocedurefromthetrigger.
Usetriggersonlyforcentralized,globaloperationsthatshouldbefiredforthetriggeringstatement,regardlessofwhichuseror
databaseapplicationissuesthestatement.
Donotcreaterecursivetriggers.Forexample,creatingan AFTER UPDATEstatementtriggeronthe Emp_tabtablethatitself
issuesan UPDATEstatementon Emp_tab,causesthetriggertofirerecursivelyuntilithasrunoutofmemory.
Usetriggerson DATABASEjudiciously.Theyareexecutedforeveryusereverytimetheeventoccursonwhichthetriggeris
created.
()
()
CreatingTriggers()()
Triggersarecreatedusingthe CREATE TRIGGERstatement.Thisstatementcanbeusedwithanyinteractivetool,suchas
SQL*PlusorEnterpriseManager.Whenusinganinteractivetool,asingleslash(/)onthelastlineisnecessarytoactivatethe
CREATE TRIGGERstatement.
()Thefollowingstatementcreatesatriggerforthe Emp_tabtable.
CREATEORREPLACETRIGGERPrint_salary_changes
BEFOREDELETEORINSERTORUPDATEONEmp_tab
FOREACHROW
WHEN(new.Empno>0)
DECLARE
sal_diffnumber
BEGIN
sal_diff:=:new.sal:old.sal
dbms_output.put('Oldsalary:'||:old.sal)
dbms_output.put('Newsalary:'||:new.sal)
dbms_output.put_line('Difference'||sal_diff)
END
/
firesthetriggeronceforeachrowthatisupdated,ineachcaseprintingthenewsalary,oldsalary,andthedifference.
The CREATE(or CREATE OR REPLACE)statementfailsifanyerrorsexistinthePL/SQLblock.
Note:
Thesizeofthetriggercannotbemorethan32K.
Thefollowingsectionsusethisexampletoillustratethewaythatpartsofatriggerarespecified.
SeeAlso:
"ExamplesofTriggerApplications"formorerealisticexamplesof CREATE TRIGGER statements
()
()
TypesofTriggers
AtriggeriseitherastoredPL/SQLblockoraPL/SQL,C,orJavaprocedureassociatedwithatable,view,schema,orthedatabase
itself.OracleDatabaseautomaticallyexecutesatriggerwhenaspecifiedeventtakesplace,whichmaybeintheformofasystem
eventoraDMLstatementbeingissuedagainstthetable.
Triggerscanbe:
DMLtriggersontables.
INSTEAD OFtriggersonviews.
SeeAlso:
OracleDatabaseSQLReference(../../server.102/b14200/toc.htm)forinformationontriggercreationsyntax
()
OverviewofSystemEvents
Youcancreatetriggerstobefiredonanyofthefollowing:
()
GettingtheAttributesofSystemEvents
Youcangetcertaineventspecificattributeswhenthetriggerisfired.
Creatingatriggeron DATABASEimpliesthatthetriggeringeventisoutsidethescopeofauser(forexample,database STARTUP
and SHUTDOWN),anditappliestoallusers(forexample,atriggercreatedon LOGONeventbytheDBA).
Creatingatriggeron SCHEMAimpliesthatthetriggeriscreatedinthecurrentuser'sschemaandisfiredonlyforthatuser.
Foreachtrigger,publicationcanbespecifiedonDMLandsystemevents.
SeeAlso:
"RespondingtoSystemEventsthroughTriggers"
()
NamingTriggers()
Triggernamesmustbeuniquewithrespecttoothertriggersinthesameschema.Triggernamesdonotneedtobeuniquewith
respecttootherschemaobjects,suchastables,views,andprocedures.Forexample,atableandatriggercanhavethesame
name(however,toavoidconfusion,thisisnotrecommended).
()
WhenIstheTriggerFired?()
()Atriggerisfiredbasedona()triggeringstatement,whichspecifies:
TheSQLstatementorthesystemevent,databaseevent,orDDLeventthatfiresthetriggerbody.Theoptionsinclude DELETE,
INSERT,and UPDATE.One,two,orallthreeoftheseoptionscanbeincludedinthetriggeringstatementspecification.
Thetable,view, DATABASE,or SCHEMAassociatedwiththetrigger.
Note:
Exactlyonetableorviewcanbespecifiedinthetriggeringstatement.Ifthe INSTEAD OF optionisused,thenthe
triggeringstatementmayonlyspecifyaviewconversely,ifaviewisspecifiedinthetriggeringstatement,thenonlythe
INSTEAD OF optionmaybeused.
DoImportandSQL*LoaderFireTriggers?
INSERTtriggersfireduringSQL*Loaderconventionalloads.(Fordirectloads,triggersaredisabledbeforetheload.)
If IGNORE= N(default)andthetablealreadyexists,thenimportdoesnotchangethetableandnoexistingtriggersfire.
Ifthetabledoesnotexist,thenimportcreatesandloadsitbeforeanytriggersaredefined,soagainnotriggersfire.
If IGNORE=Y,thenimportloadsrowsintoexistingtables.Anyexistingtriggersfire,andindexesareupdatedtoaccountforthe
importeddata.
()
HowColumnListsAffectUPDATETriggers()()()()()
An UPDATEstatementmightincludealistofcolumns.Ifatriggeringstatementincludesacolumnlist,thetriggerisfiredonlywhen
oneofthespecifiedcolumnsisupdated.Ifatriggeringstatementomitsacolumnlist,thetriggerisfiredwhenanycolumnofthe
associatedtableisupdated.Acolumnlistcannotbespecifiedfor INSERTor DELETEtriggeringstatements.
Thepreviousexampleofthe PRINT_SALARY_CHANGEStriggercouldincludeacolumnlistinthetriggeringstatement.Forexample:
...BEFOREDELETEORINSERTORUPDATEOFenameONEmp_tab...
Notes:
()
ControllingWhenaTriggerIsFired(BEFOREandAFTEROptions)()()()()()
()
The BEFOREor AFTERoptioninthe CREATE TRIGGERstatementspecifiesexactlywhentofirethetriggerbodyinrelationtothe
triggeringstatementthatisbeingrun.Ina CREATE TRIGGERstatement,the BEFOREor AFTERoptionisspecifiedjustbeforethe
triggeringstatement.Forexample,the PRINT_SALARY_CHANGEStriggerinthepreviousexampleisa BEFOREtrigger.
Ingeneral,youuse BEFOREor AFTERtriggerstoachievethefollowingresults:
Use BEFORErowtriggerstomodifytherowbeforetherowdataiswrittentodisk.
Use AFTERrowtriggerstoobtain,andperformoperations,usingtherowID.
Note:
BEFORE rowtriggersareslightlymoreefficientthan AFTER rowtriggers.With AFTER rowtriggers,affecteddata
blocksmustberead(logicalread,notphysicalread)onceforthetriggerandthenagainforthetriggeringstatement.
Alternatively,with BEFORE rowtriggers,thedatablocksmustbereadonlyonceforboththetriggeringstatementand
thetrigger.
()BEFORETriggersFiredMultipleTimes()
Ifan UPDATEor DELETEstatementdetectsaconflictwithaconcurrent UPDATE,thenOracleDatabaseperformsatransparent
ROLLBACKto SAVEPOINTandrestartstheupdate.Thiscanoccurmanytimesbeforethestatementcompletessuccessfully.Each
timethestatementisrestarted,the BEFOREstatementtriggerisfiredagain.Therollbacktosavepointdoesnotundochangestoany
packagevariablesreferencedinthetrigger.Yourpackageshouldincludeacountervariabletodetectthissituation.
()
OrderingofTriggers()
()()ArelationaldatabasedoesnotguaranteetheorderofrowsprocessedbyaSQLstatement.Therefore,donotcreatetriggersthat
dependontheorderinwhichrowsareprocessed.Forexample,donotassignavaluetoaglobalpackagevariableinarowtriggerif
thecurrentvalueoftheglobalvariableisdependentontherowbeingprocessedbytherowtrigger.Also,ifglobalpackagevariables
areupdatedwithinatrigger,thenitisbesttoinitializethosevariablesina BEFOREstatementtrigger.
Whenastatementinatriggerbodycausesanothertriggertobefired,thetriggersaresaidtobecascading.OracleDatabaseallows
upto32triggerstocascadeatanyonetime.However,youcaneffectivelylimitthenumberoftriggercascadesusingthe
initializationparameter OPEN_CURSORS,becauseacursormustbeopenedforeveryexecutionofatrigger.
()TriggerEvaluationOrder
()()()Althoughanytriggercanrunasequenceofoperationseitherinlineorbycallingprocedures,usingmultipletriggersofthe
sametypeenhancesdatabaseadministrationbypermittingthemodularinstallationofapplicationsthathavetriggersonthesame
tables.
OracleDatabaseexecutesalltriggersofthesametypebeforeexecutingtriggersofadifferenttype.Ifyouhavemultipletriggersof
thesametypeonasingletable,thenOracleDatabasechoosesanarbitraryordertoexecutethesetriggers.
SeeAlso:
OracleDatabaseConcepts(../../server.102/b14220/toc.htm)formoreinformationonthefiringorderoftriggers
Eachsubsequenttriggerseesthechangesmadebythepreviouslyfiredtriggers.Eachtriggercanseetheoldandnewvalues.The
oldvaluesaretheoriginalvalues,andthenewvaluesarethecurrentvalues,assetbythemostrecentlyfired UPDATEor INSERT
trigger.
Toensurethatmultipletriggeredactionsoccurinaspecificorder,youmustconsolidatetheseactionsintoasingletrigger(for
example,byhavingthetriggercallaseriesofprocedures).
()
()
ModifyingComplexViews(INSTEADOFTriggers)
An()updatableviewisonethatletsyouperformDMLontheunderlyingtable.Someviewsareinherentlyupdatable,butothersare
notbecausetheywerecreatedwithoneormoreoftheconstructslistedin"ViewsthatRequireINSTEADOFTriggers".
Anyviewthatcontainsoneofthoseconstructscanbemadeupdatablebyusingan INSTEADOFtrigger.()() INSTEAD OFtriggers
provideatransparentwayofmodifyingviewsthatcannotbemodifieddirectlythrough UPDATE, INSERT,and DELETEstatements.
Thesetriggersarecalled INSTEAD OFtriggersbecause,unlikeothertypesoftriggers,OracleDatabasefiresthetriggerinsteadof
executingthetriggeringstatement.Thetriggermustdeterminewhatoperationwasintendedandperform UPDATE, INSERT,or
DELETEoperationsdirectlyontheunderlyingtables.
SeeAlso:
"FiringTriggersOneorManyTimes(FOREACHROWOption)"
Note:
The INSTEAD OFoptioncanonlybeusedfortriggerscreatedoverviews.
The BEFOREand AFTERoptionscannotbeusedfortriggerscreatedoverviews.
The CHECKoptionforviewsisnotenforcedwheninsertsorupdatestotheviewaredoneusing INSTEAD OFtriggers.
The INSTEAD OFtriggerbodymustenforcethecheck.
()
()
ViewsthatRequireINSTEADOFTriggers
()()Aviewcannotbemodifiedby UPDATE, INSERT,or DELETEstatementsiftheviewquerycontainsanyofthefollowing
constructs:
Asetoperator
A DISTINCToperator
Anaggregateoranalyticfunction
A GROUP BY, ORDERBY, MODEL, CONNECT BY,or START WITHclause
Acollectionexpressionina SELECTlist
Asubqueryina SELECTlist
Asubquerydesignated WITHREADONLY
Joins,withsomeexceptions,asdocumentedinOracleDatabaseAdministrator'sGuide(../../server.102/b14231/toc.htm)
()()()Ifaviewcontainspseudocolumnsorexpressions,thenyoucanonlyupdatetheviewwithan UPDATEstatementthatdoesnot
refertoanyofthepseudocolumnsorexpressions.
()
INSTEADOFTriggerExample
Note:
Youmayneedtosetupthefollowingdatastructuresforthisexampletowork:
CREATETABLEProject_tab(
Prj_levelNUMBER,
ProjnoNUMBER,
Resp_deptNUMBER)
CREATETABLEEmp_tab(
EmpnoNUMBERNOTNULL,
EnameVARCHAR2(10),
JobVARCHAR2(9),
MgrNUMBER(4),
HiredateDATE,
SalNUMBER(7,2),
CommNUMBER(7,2),
DeptnoNUMBER(2)NOTNULL)
CREATETABLEDept_tab(
DeptnoNUMBER(2)NOTNULL,
DnameVARCHAR2(14),
LocVARCHAR2(13),
Mgr_noNUMBER,
Dept_typeNUMBER)
CREATEORREPLACEVIEWmanager_infoAS
SELECTe.ename,e.empno,d.dept_type,d.deptno,p.prj_level,
p.projno
FROMEmp_tabe,Dept_tabd,Project_tabp
WHEREe.empno=d.mgr_no
ANDd.deptno=p.resp_dept
CREATEORREPLACETRIGGERmanager_info_insert
INSTEADOFINSERTONmanager_info
REFERENCINGNEWASnnewmanagerinformation
FOREACHROW
DECLARE
rowcntnumber
BEGIN
SELECTCOUNT(*)INTOrowcntFROMEmp_tabWHEREempno=:n.empno
IFrowcnt=0THEN
INSERTINTOEmp_tab(empno,ename)VALUES(:n.empno,:n.ename)
ELSE
UPDATEEmp_tabSETEmp_tab.ename=:n.ename
WHEREEmp_tab.empno=:n.empno
ENDIF
SELECTCOUNT(*)INTOrowcntFROMDept_tabWHEREdeptno=:n.deptno
IFrowcnt=0THEN
INSERTINTODept_tab(deptno,dept_type)
VALUES(:n.deptno,:n.dept_type)
ELSE
UPDATEDept_tabSETDept_tab.dept_type=:n.dept_type
WHEREDept_tab.deptno=:n.deptno
ENDIF
SELECTCOUNT(*)INTOrowcntFROMProject_tab
WHEREProject_tab.projno=:n.projno
IFrowcnt=0THEN
INSERTINTOProject_tab(projno,prj_level)
VALUES(:n.projno,:n.prj_level)
ELSE
UPDATEProject_tabSETProject_tab.prj_level=:n.prj_level
WHEREProject_tab.projno=:n.projno
ENDIF
END
Theactionsshownforrowsbeinginsertedintothe MANAGER_INFOviewfirsttesttoseeifappropriaterowsalreadyexistinthebase
tablesfromwhich MANAGER_INFOisderived.Theactionstheninsertnewrowsorupdateexistingrows,asappropriate.Similar
triggerscanspecifyappropriateactionsfor UPDATEand DELETE.
()
ObjectViewsandINSTEADOFTriggers
INSTEAD OFtriggersprovidethemeanstomodifyobjectviewinstancesontheclientsidethroughOCIcalls.
SeeAlso:
OracleCallInterfaceProgrammer'sGuide(../../appdev.102/b14250/toc.htm)
Tomodifyanobjectmaterializedbyanobjectviewintheclientsideobjectcacheandflushitbacktothepersistentstore,youmust
specify INSTEAD OFtriggers,unlesstheobjectviewismodifiable.Iftheobjectisreadonly,thenitisnotnecessarytodefine
triggerstopinit.
()
TriggersonNestedTableViewColumns
INSTEAD OFtriggerscanalsobecreatedovernestedtableviewcolumns.Thesetriggersprovideawayofupdatingelementsofthe
nestedtable.Theyfireforeachnestedtableelementbeingmodified.Therowcorrelationvariablesinsidethetriggercorrespondto
thenestedtableelement.Thistypeoftriggeralsoprovidesanadditionalcorrelationnameforaccessingtheparentrowthatcontains
thenestedtablebeingmodified.
Note:
Thesetriggers:
Canonlybedefinedovernestedtablecolumnsinviews.
Forexample,consideradepartmentviewthatcontainsanestedtableofemployees.
CREATEORREPLACEVIEWDept_viewAS
SELECTd.Deptno,d.Dept_type,d.Dept_name,
CAST(MULTISET(SELECTe.Empno,e.Empname,e.Salary)
FROMEmp_tabe
WHEREe.Deptno=d.Deptno)ASAmp_list_Emplist
FROMDept_tabd
FiringTriggersOneorManyTimes(FOREACHROWOption)()()()()
The FOR EACH ROWoptiondetermineswhetherthetriggerisarowtriggerorastatementtrigger.Ifyouspecify FOR EACH ROW,
thenthetriggerfiresonceforeachrowofthetablethatisaffectedbythetriggeringstatement.Theabsenceofthe FOR EACH ROW
optionindicatesthatthetriggerfiresonlyonceforeachapplicablestatement,butnotseparatelyforeachrowaffectedbythe
statement.
()Forexample,youdefinethefollowingtrigger:
Note:
Youmayneedtosetupthefollowingdatastructuresforcertainexamplestowork:
CREATETABLEEmp_log(
Emp_idNUMBER,
Log_dateDATE,
New_salaryNUMBER,
ActionVARCHAR2(20))
CREATEORREPLACETRIGGERLog_salary_increase
AFTERUPDATEONEmp_tab
FOREACHROW
WHEN(new.Sal>1000)
BEGIN
INSERTINTOEmp_log(Emp_id,Log_date,New_salary,Action)
VALUES(:new.Empno,SYSDATE,:new.SAL,'NEWSAL')
END
Then,youenterthefollowingSQLstatement:
UPDATEEmp_tabSETSal=Sal+1000.0
WHEREDeptno=20
Iftherearefiveemployeesindepartment20,thenthetriggerfiresfivetimeswhenthisstatementisentered,becausefiverowsare
affected.
Thefollowingtriggerfiresonlyonceforeach UPDATEofthe Emp_tabtable:
CREATEORREPLACETRIGGERLog_emp_update
AFTERUPDATEONEmp_tab
BEGIN
INSERTINTOEmp_log(Log_date,Action)
VALUES(SYSDATE,'Emp_tabCOMMISSIONSCHANGED')
END
SeeAlso:
OracleDatabaseConcepts(../../server.102/b14220/toc.htm)fortheorderoftriggerfiring
Thestatementleveltriggersareusefulforperformingvalidationchecksfortheentirestatement.
()
FiringTriggersBasedonConditions(WHENClause)()()()
Optionally,atriggerrestrictioncanbeincludedinthedefinitionofarowtriggerbyspecifyingaBooleanSQLexpressionina WHEN
clause.
Note:
A WHEN clausecannotbeincludedinthedefinitionofastatementtrigger.
Ifincluded,thentheexpressioninthe WHENclauseisevaluatedforeachrowthatthetriggeraffects.
Iftheexpressionevaluatesto TRUEforarow,thenthetriggerbodyisfiredonbehalfofthatrow.However,iftheexpression
evaluatesto FALSEor NOT TRUEforarow(unknown,aswithnulls),thenthetriggerbodyisnotfiredforthatrow.Theevaluationof
the WHENclausedoesnothaveaneffectontheexecutionofthetriggeringSQLstatement(inotherwords,thetriggeringstatement
isnotrolledbackiftheexpressionina WHENclauseevaluatesto FALSE).
Forexample,inthe PRINT_SALARY_CHANGEStrigger,thetriggerbodyisnotrunifthenewvalueof Empnoiszero, NULL,or
negative.Inmorerealisticexamples,youmighttestifonecolumnvalueislessthananother.
()()Theexpressionina WHENclauseofarowtriggercanincludecorrelationnames,whichareexplainedlater.Theexpressionina
WHENclausemustbeaSQLexpression,anditcannotincludeasubquery.YoucannotuseaPL/SQLexpression(includinguser
definedfunctions)inthe WHENclause.
Note:
Youcannotspecifythe WHEN clausefor INSTEAD OF triggers.
()
()
CodingtheTriggerBody()
()()()Thetriggerbodyisa CALLprocedureoraPL/SQLblockthatcanincludeSQLandPL/SQLstatements.The CALLprocedure
canbeeitheraPL/SQLoraJavaprocedurethatisencapsulatedinaPL/SQLwrapper.Thesestatementsarerunifthetriggering
statementisenteredandifthetriggerrestriction(ifincluded)evaluatesto TRUE.
ThetriggerbodyforrowtriggershassomespecialconstructsthatcanbeincludedinthecodeofthePL/SQLblock:correlation
namesandthe REFERENCEINGoption,andtheconditionalpredicates INSERTING, DELETING,and UPDATING.
Note:
The INSERTING , DELETING ,and UPDATING conditionalpredicatescannotbeusedforthe CALL proceduresthey
canonlybeusedinaPL/SQLblock.
()
Example:MonitoringLogonswithaTrigger
Note:
Youmayneedtosetupdatastructuressimilartothefollowingforcertainexamplestowork:
CONNECTsystem/manager
GRANTADMINISTERDATABASETRIGGERTOscott
CONNECTscott/tiger
CREATETABLEaudit_table(
seqnumber,
user_atVARCHAR2(10),
time_nowDATE,
termVARCHAR2(10),
jobVARCHAR2(10),
procVARCHAR2(10),
enumNUMBER)
CREATEORREPLACEPROCEDUREfoo(cVARCHAR2)AS
BEGIN
INSERTINTOAudit_table(user_at)VALUES(c)
END
CREATEORREPLACETRIGGERlogontrigAFTERLOGONONDATABASE
Justcallanexistingprocedure.TheORA_LOGIN_USERisafunction
thatreturnsinformationabouttheeventthatfiredthetrigger.
CALLfoo(ora_login_user)
/
()
Example:CallingaJavaProcedurefromaTrigger
AlthoughtriggersaredeclaredusingPL/SQL,theycancallproceduresinotherlanguages,suchasJava:
CREATEORREPLACEPROCEDUREBefore_delete(IdINNUMBER,EnameVARCHAR2)
ISlanguageJava
name'thjvTriggers.beforeDelete(oracle.sql.NUMBER,oracle.sql.CHAR)'
CREATEORREPLACETRIGGERPre_del_triggerBEFOREDELETEONTab
FOREACHROW
CALLBefore_delete(:old.Id,:old.Ename)
/
ThecorrespondingJavafileis thjvTriggers.java:
importjava.sql.*
importjava.io.*
importoracle.sql.*
importoracle.oracore.*
publicclassthjvTriggers
{
publicstatevoid
beforeDelete(NUMBERold_id,CHARold_name)
ThrowsSQLException,CoreException
{
Connectionconn=JDBCConnection.defaultConnection()
Statementstmt=conn.CreateStatement()
Stringsql="insertintologtabvalues
("+old_id.intValue()+",'"+old_ename.toString()+",BEFOREDELETE')
stmt.executeUpdate(sql)
stmt.close()
return
}
}
()
AccessingColumnValuesinRowTriggers()()()()
Withinatriggerbodyofarowtrigger,thePL/SQLcodeandSQLstatementshaveaccesstotheoldandnewcolumnvaluesofthe
currentrowaffectedbythetriggeringstatement.Twocorrelationnamesexistforeverycolumnofthetablebeingmodified:onefor
theoldcolumnvalue,andoneforthenewcolumnvalue.Dependingonthetypeoftriggeringstatement,certaincorrelationnames
mightnothaveanymeaning.
()Atriggerfiredbyan INSERTstatementhasmeaningfulaccesstonewcolumnvaluesonly.Becausetherowisbeingcreatedby
the INSERT,theoldvaluesarenull.
()Atriggerfiredbyan UPDATEstatementhasaccesstobotholdandnewcolumnvaluesforboth BEFOREand AFTERrow
triggers.
()Atriggerfiredbya DELETEstatementhasmeaningfulaccessto: oldcolumnvaluesonly.Becausetherownolongerexists
()()()()Thenewcolumnvaluesarereferencedusingthe newqualifierbeforethecolumnname,whiletheoldcolumnvaluesare
qualifierswhentheyareusedinatriggerbody,butacolonisnotallowedwhenusingthequalifiersinthe WHENclauseorthe
REFERENCINGoption.
()
()
()Example:ModifyingLOBColumnswithaTrigger
YoucantreatLOBcolumnsthesameasothercolumns,usingregularSQLandPL/SQLfunctionswith CLOBcolumns,andcallsto
the DBMS_LOBpackagewith BLOBcolumns:
droptabletab1
createtabletab1(c1clob)
insertintotab1values('<h1>HTMLDocumentFragment</h1><p>Sometext.')
createorreplacetriggertrg1
beforeupdateontab1
foreachrow
begin
dbms_output.put_line('OldvalueofCLOBcolumn:'||:OLD.c1)
dbms_output.put_line('ProposednewvalueofCLOBcolumn:'||:NEW.c1)
Previously,wecouldn'tchangethenewvalueforaLOB.
Now,wecanreplaceit,orconstructanewvalueusingSUBSTR,INSTR...
operationsforaCLOB,orDBMS_LOBcallsforaBLOB.
:NEW.c1:=:NEW.c1||to_clob('<hr><p>Standardfooterparagraph.')
dbms_output.put_line('FinalvalueofCLOBcolumn:'||:NEW.c1)
end
/
setserveroutputon
updatetab1setc1='<h1>DifferentDocumentFragment</h1><p>Differenttext.'
select*fromtab1
()
INSTEADOFTriggersonNestedTableViewColumns()
Inthecaseof INSTEAD OFtriggersonnestedtableviewcolumns,the newand oldqualifierscorrespondtothenewandold
nestedtableelements.Theparentrowcorrespondingtothisnestedtableelementcanbeaccessedusingthe parentqualifier.The
parentcorrelationnameismeaningfulandvalidonlyinsideanestedtabletrigger.
()
AvoidingNameConflictswithTriggers(REFERENCINGOption)()()()()
The REFERENCINGoptioncanbespecifiedinatriggerbodyofarowtriggertoavoidnameconflictsamongthecorrelationnames
andtablesthatmightbenamed oldor new.Becausethisisrare,thisoptionisinfrequentlyused.
()Forexample,assumeyouhaveatablenamed newwithcolumns field1(number)and field2(character).Thefollowing
Note:
Youmayneedtosetupthefollowingdatastructuresforcertainexamplestowork:
CREATETABLEnew(
field1NUMBER,
field2VARCHAR2(20))
CREATEORREPLACETRIGGERPrint_salary_changes
BEFOREUPDATEONnew
REFERENCINGnewASNewest
FOREACHROW
BEGIN
:Newest.Field2:=TO_CHAR(:newest.field1)
END
DetectingtheDML()O()per()ationThatFiredaTrigger
()IfmorethanonetypeofDMLoperationcanfireatrigger(forexample, ON INSERT OR DELETE OR UPDATE OF Emp_tab),the
columnisbeingupdated.Forexample,assumeatriggerisdefinedasthefollowing:
CREATEORREPLACETRIGGER...
...UPDATEOFSal,CommONEmp_tab...
BEGIN
...IFUPDATING('SAL')THEN...ENDIF
END
ErrorConditionsandExceptionsintheTriggerBody()()()
()Ifapredefinedoruserdefinederrorconditionorexceptionisraisedduringtheexecutionofatriggerbody,thenalleffectsofthe
triggerbody,aswellasthetriggeringstatement,arerolledback(unlesstheerroristrappedbyanexceptionhandler).Therefore,a
triggerbodycanpreventtheexecutionofthetriggeringstatementbyraisinganexception.Userdefinedexceptionsarecommonly
usedintriggersthatenforcecomplexsecurityauthorizationsorintegrityconstraints.
Theonlyexceptiontothisiswhentheeventunderconsiderationisdatabase STARTUP, SHUTDOWN,or LOGINwhentheuser
logginginis SYSTEM.Inthesescenarios,onlythetriggeractionisrolledback.
()
()TriggersonObjectTables
Youcanusethe() OBJECT_VALUEpseudocolumninatriggeronanobjecttablesince10gRelease1(10.1). OBJECT_VALUEmeans
theobjectasawhole.Thisisoneexampleofitsuse.YoucanalsocallaPL/SQLfunctionwith OBJECT_VALUEasthedatatypeofan
INformalparameter.
UPDATEtblSETtbl.n=tbl.n+1
/
BEGIN
FORjIN(SELECTd,old_obj,new_objFROMtbl_history)LOOP
Dbms_Output.Put_Line(
j.d||
'old:'||j.old_obj.n||''||j.old_obj.m||
'new:'||j.new_obj.n||''||j.new_obj.m)
ENDLOOP
END
/
TriggersandHandlingRemoteExceptions()()
()Atriggerthataccessesaremotesitecannotdoremoteexceptionhandlingifthenetworklinkisunavailable.Forexample:
CREATEORREPLACETRIGGERExample
AFTERINSERTONEmp_tab
FOREACHROW
BEGIN
INSERTINTOEmp_tab@Remote<compilationfailshere
VALUES('x')whendblinkisinaccessible
EXCEPTION
WHENOTHERSTHEN
INSERTINTOEmp_log
VALUES('x')
END
Atriggeriscompiledwhenitiscreated.Thus,ifaremotesiteisunavailablewhenthetriggermustcompile,thenOracleDatabase
cannotvalidatethestatementaccessingtheremotedatabase,andthecompilationfails.Thepreviousexampleexceptionstatement
cannotrun,becausethetriggerdoesnotcompletecompilation.
Becausestoredproceduresarestoredinacompiledform,theworkaroundforthepreviousexampleisasfollows:
CREATEORREPLACETRIGGERExample
AFTERINSERTONEmp_tab
FOREACHROW
BEGIN
Insert_row_proc
END
CREATEORREPLACEPROCEDUREInsert_row_procAS
BEGIN
INSERTINTOEmp_tab@Remote
VALUES('x')
EXCEPTION
WHENOTHERSTHEN
INSERTINTOEmp_log
VALUES('x')
END
Thetriggerinthisexamplecompilessuccessfullyandcallsthestoredprocedure,whichalreadyhasavalidatedstatementfor
accessingtheremotedatabasethus,whentheremote INSERTstatementfailsbecausethelinkisdown,theexceptioniscaught.
()
RestrictionsonCreatingTriggers()()
CodingtriggersrequiressomerestrictionsthatarenotrequiredforstandardPL/SQLblocks.Thefollowingsectionsdiscussthese
restrictions.
()MaximumTriggerSize
Thesizeofatriggercannotbemorethan32K.
()SQLStatementsAllowedinTriggerBodies
()()()ThebodyofatriggercancontainDMLSQLstatements.Itcanalsocontain SELECTstatements,buttheymustbe SELECT...
INTO...statementsorthe SELECTstatementinthedefinitionofacursor.
()()DDLstatementsarenotallowedinthebodyofatrigger.Also,notransactioncontrolstatementsareallowedinatrigger.
Note:
Aprocedurecalledbyatriggercannotruntheprevioustransactioncontrolstatements,becausetheprocedurerunswithin
thecontextofthetriggerbody.
()()()()()Statementsinsideatriggercanreferenceremoteschemaobjects.However,payspecialattentionwhencallingremote
proceduresfromwithinalocaltrigger.Ifatimestamporsignaturemismatchisfoundduringexecutionofthetrigger,thentheremote
procedureisnotrun,andthetriggerisinvalidated.
()()()TriggerRestrictionsonLONGandLONGRAWDatatypes
LONGand LONG RAWdatatypesintriggersaresubjecttothefollowingrestrictions:
()TriggerRestrictionsonMutatingTables()()()()()
A()()mutatingtableisatablethatisbeingmodifiedbyan UPDATE, DELETE,or INSERTstatement,oratablethatmightbe
updatedbytheeffectsofa DELETE CASCADEconstraint.
Thesessionthatissuedthetriggeringstatementcannotqueryormodifyamutatingtable.Thisrestrictionpreventsatriggerfrom
seeinganinconsistentsetofdata.
Thisrestrictionappliestoalltriggersthatusethe FOREACHROWclause.Viewsbeingmodifiedin INSTEAD OFtriggersarenot
consideredmutating.
Whenatriggerencountersamutatingtable,aruntimeerroroccurs,theeffectsofthetriggerbodyandtriggeringstatementare
rolledback,andcontrolisreturnedtotheuserorapplication.
Considerthefollowingtrigger:
CREATEORREPLACETRIGGEREmp_count
AFTERDELETEONEmp_tab
FOREACHROW
DECLARE
nINTEGER
BEGIN
SELECTCOUNT(*)INTOnFROMEmp_tab
DBMS_OUTPUT.PUT_LINE('Therearenow'||n||
'employees.')
END
IfthefollowingSQLstatementisentered:
DELETEFROMEmp_tabWHEREEmpno=7499
Anerrorisreturnedbecausethetableismutatingwhentherowisdeleted:
ORA04091:tableSCOTT.Emp_tabismutating,trigger/functionmaynotseeit
SeeAlso:
OracleDatabaseConcepts(../../server.102/b14220/toc.htm)forinformationabouttheinteractionoftriggersandintegrity
constraints
Becausedeclarativereferentialintegrityconstraintsarenotsupportedbetweentablesondifferentnodesofadistributeddatabase,
themutatingtablerestrictionsdonotapplytotriggersthataccessremotenodes.Theserestrictionsarealsonotenforcedamong
tablesinthesamedatabasethatareconnectedbyloopbackdatabaselinks.Aloopbackdatabaselinkmakesalocaltableappear
remotebydefininganOracleNetpathbacktothedatabasethatcontainsthelink.
()RestrictionsonMutatingTablesRelaxed
Themutatingerror,discussedearlierinthissection,stillpreventsthetriggerfromreadingormodifyingthetablethattheparent
statementismodifying.However,startinginOracleDatabaseRelease8.1,adeleteagainsttheparenttablecausesbefore/after
statementtriggerstobefiredonce.Thatway,youcancreatetriggers(justnotrowtriggers)toreadandmodifytheparentandchild
tables.
Thisallowsmostforeignkeyconstraintactionstobeimplementedthroughtheirobviousafterrowtrigger,providingtheconstraintis
notselfreferential.Updatecascade,updatesetnull,updatesetdefault,deletesetdefault,insertingamissingparent,and
maintainingacountofchildrencanallbeimplementedeasily.Forexample,thisisanimplementationofupdatecascade:
createtablep(p1numberconstraintppkprimarykey)
createtablef(f1numberconstraintffkreferencesp)
createtriggerptafterupdateonpforeachrowbegin
updatefsetf1=:new.p1wheref1=:old.p1
end
/
Thisimplementationrequirescareformultirowupdates.Forexample,ifatablephasthreerowswiththevalues(1),(2),(3),and
tablefalsohasthreerowswiththevalues(1),(2),(3),thenthefollowingstatementupdatespcorrectlybutcausesproblemswhen
thetriggerupdatesf:
updatepsetp1=p1+1
Thestatementfirstupdates(1)to(2)inp,andthetriggerupdates(1)to(2)inf,leavingtworowsofvalue(2)inf.Thenthe
statementupdates(2)to(3)inp,andthetriggerupdatesbothrowsofvalue(2)to(3)inf.Finally,thestatementupdates(3)to(4)in
p,andthetriggerupdatesallthreerowsinffrom(3)to(4).Therelationshipofthedatainpandfislost.
Toavoidthisproblem,youmustforbidmultirowupdatestopthatchangetheprimarykeyandreuseexistingprimarykeyvalues.It
couldalsobesolvedbytrackingwhichforeignkeyvalueshavealreadybeenupdated,thenmodifyingthetriggersothatnorowis
updatedtwice.
Thatistheonlyproblemwiththistechniqueforforeignkeyupdates.Thetriggercannotmissrowsthathavebeenchangedbutnot
committedbyanothertransaction,becausetheforeignkeyconstraintguaranteesthatnomatchingforeignkeyrowsarelocked
beforetheafterrowtriggeriscalled.
()()SystemTriggerRestrictions
Dependingontheevent,differenteventattributefunctionsareavailable.Forexample,certainDDLoperationsmaynotbeallowed
onDDLevents.Check"EventAttributeFunctions"beforeusinganeventattributefunction,becauseitseffectsmightbeundefined
ratherthanproducinganerrorcondition.
Onlycommittedtriggersarefired.Forexample,ifyoucreateatriggerthatshouldbefiredafterall CREATEevents,thenthetrigger
itselfdoesnotfireafterthecreation,becausethecorrectinformationaboutthistriggerwasnotcommittedatthetimewhenthe
triggeron CREATEeventswasfired.
Forexample,ifyouexecutethefollowingSQLstatement:
CREATEORREPLACETRIGGERmy_triggerAFTERCREATEONDATABASE
BEGINnull
END
Allrestrictionsonforeignfunctioncalloutsalsoapply.
()
WhoIstheTriggerUser?
()()Thefollowingstatement,insideatrigger,returnstheownerofthetrigger,notthenameofuserwhoisupdatingthetable:
SELECTUsernameFROMUSER_USERS
()
PrivilegesNeededtoWorkwithTriggers()()
()()()Tocreateatriggerinyourschema,youmusthavethe CREATE TRIGGERsystemprivilege,andeither:
Ownthetablespecifiedinthetriggeringstatement,or
Havethe ALTERprivilegeforthetableinthetriggeringstatement,or
Havethe ALTER ANY TABLEsystemprivilege
Tocreateatriggerinanotheruser'sschema,ortoreferenceatableinanotherschemafromatriggerinyourschema,youmust
havethe CREATE ANY TRIGGERsystemprivilege.Withthisprivilege,thetriggercanbecreatedinanyschemaandcanbe
associatedwithanyuser'stable.Inaddition,theusercreatingthetriggermustalsohave EXECUTEprivilegeonthereferenced
procedures,functions,orpackages.
Tocreateatriggeron DATABASE,youmusthavethe ADMINISTER DATABASE TRIGGERprivilege.Ifthisprivilegeislaterrevoked,
thenyoucandropthetrigger,butnotalterit.
Theobjectprivilegestotheschemaobjectsreferencedinthetriggerbodymustbegrantedtothetriggerownerexplicitly(not
througharole).Thestatementsinthetriggerbodyoperateundertheprivilegedomainofthetriggerowner,nottheprivilegedomain
oftheuserissuingthetriggeringstatement.Thisissimilartotheprivilegemodelforstoredprocedures.
()
()
CompilingTriggers()()
TriggersaresimilartoPL/SQLanonymousblockswiththeadditionofthe: newand: oldcapabilities,buttheircompilationis
different.APL/SQLanonymousblockiscompiledeachtimeitisloadedintomemory.Compilationinvolvesthreestages:
1. Syntaxchecking:PL/SQLsyntaxischecked,andaparsetreeisgenerated().
2. Semanticchecking:Typecheckingandfurtherprocessingontheparsetree.
3. Codegeneration:Thepcodeisgenerated.()
DependenciesforTriggers()
Compiledtriggershavedependencies.Theybecomeinvalidifadependedonobject,suchasastoredprocedureorfunctioncalled
fromthetriggerbody,ismodified.Triggersthatareinvalidatedfordependencyreasonsarerecompiledwhennextinvoked.
Youcanexaminethe ALL_DEPENDENCIESviewtoseethedependenciesforatrigger.Forexample,thefollowingstatementshows
thedependenciesforthetriggersinthe SCOTTschema:
SELECTNAME,REFERENCED_OWNER,REFERENCED_NAME,REFERENCED_TYPE
FROMALL_DEPENDENCIES
WHEREOWNER='SCOTT'andTYPE='TRIGGER'
Triggersmaydependonotherfunctionsorpackages.Ifthefunctionorpackagespecifiedinthetriggerisdropped,thenthetrigger
ismarkedinvalid.Anattemptismadetovalidatethetriggeronoccurrenceoftheevent.Ifthetriggercannotbevalidated
successfully,thenitismarked VALID WITH ERRORS,andtheeventfails.
Note:
Thereisanexceptionfor STARTUPevents: STARTUPeventssucceedevenifthetriggerfails.Therearealsoexceptions
for SHUTDOWNeventsandfor LOGONeventsifyouloginas SYSTEM.
Becausethe DBMS_AQpackageisusedtoenqueueamessage,dependencybetweentriggersandqueuescannotbe
maintained.
()
RecompilingTriggers()
Usethe ALTER TRIGGERstatementtorecompileatriggermanually.Forexample,thefollowingstatementrecompilesthe
PRINT_SALARY_CHANGEStrigger:
ALTERTRIGGERPrint_salary_changesCOMPILE
()
()
ModifyingTriggers()
Likeastoredprocedure,atriggercannotbeexplicitlyaltered:Itmustbereplacedwithanewdefinition.(The ALTER TRIGGER
statementisusedonlytorecompile,enable,ordisableatrigger.)
Whenreplacingatrigger,youmustincludethe OR REPLACEoptioninthe CREATE TRIGGERstatement.The OR REPLACEoptionis
providedtoallowanewversionofanexistingtriggertoreplacetheolderversion,withoutaffectinganygrantsmadefortheoriginal
versionofthetrigger.
Alternatively,thetriggercanbedroppedusingthe DROP TRIGGERstatement,andyoucanrerunthe CREATE TRIGGERstatement.()
()()()
DebuggingTriggers()()
Youcandebugatriggerusingthesamefacilitiesavailableforstoredprocedures.
SeeAlso:
"DebuggingStoredProcedures"(adfns_packages.htm#i1007648)
()
()
EnablingandDisablingTriggers
()()()()Atriggercanbeinoneoftwodistinctmodes:
()()Enabled.Anenabledtriggerexecutesitstriggerbodyifatriggeringstatementisenteredandthetriggerrestriction(ifany)
evaluatesto TRUE.
()()Disabled.Adisabledtriggerdoesnotexecuteitstriggerbody,evenifatriggeringstatementisenteredandthetriggerrestriction
(ifany)evaluatesto TRUE.
()
EnablingTriggers()()
Bydefault,atriggerisautomaticallyenabledwhenitiscreatedhowever,itcanlaterbedisabled.Afteryouhavecompletedthetask
thatrequiredthetriggertobedisabled,reenablethetrigger,sothatitfireswhenappropriate.
()Enableadisabledtriggerusingthe ALTER TRIGGERstatementwiththe ENABLEoption.Toenablethedisabledtriggernamed
REORDERofthe INVENTORYtable,enterthefollowingstatement:
ALTERTRIGGERReorderENABLE
DisablingTriggers()()
Youmighttemporarilydisableatriggerif:
Anobjectitreferencesisnotavailable.
Youneedtoperformalargedataload,andyouwantittoproceedquicklywithoutfiringtriggers.
Youarereloadingdata.
option.
Forexample,todisablethetriggernamed REORDERofthe INVENTORYtable,enterthefollowingstatement:
ALTERTRIGGERReorderDISABLE
ViewingInformationAboutTriggers()
Thefollowingdatadictionaryviewsrevealinformationabouttriggers:
USER_TRIGGERS
ALL_TRIGGERS
DBA_TRIGGERS
SeeAlso:
OracleDatabaseReference(../../server.102/b14237/toc.htm)foracompletedescriptionofthesedatadictionaryviews
Forexample,assumethefollowingstatementwasusedtocreatethe REORDERtrigger:
Caution:
Youmayneedtosetupdatastructuresforcertainexamplestowork:
()CREATEORREPLACETRIGGERReorder
AFTERUPDATEOFParts_on_handONInventory
FOREACHROW
WHEN(new.Parts_on_hand<new.Reorder_point)
DECLARE
xNUMBER
BEGIN
SELECTCOUNT(*)INTOx
FROMPending_orders
WHEREPart_no=:new.Part_no
IFx=0THEN
INSERTINTOPending_orders
VALUES(:new.Part_no,:new.Reorder_quantity,
sysdate)
ENDIF
END
Thefollowingtwoqueriesreturninformationaboutthe REORDERtrigger:
SELECTTrigger_type,Triggering_event,Table_name
FROMUSER_TRIGGERS
WHERETrigger_name='REORDER'
TYPETRIGGERING_STATEMENTTABLE_NAME
AFTEREACHROWUPDATEINVENTORY
SELECTTrigger_body
FROMUSER_TRIGGERS
WHERETrigger_name='REORDER'
TRIGGER_BODY
DECLARE
xNUMBER
BEGIN
SELECTCOUNT(*)INTOx
FROMPending_orders
WHEREPart_no=:new.Part_no
IFx=0
THENINSERTINTOPending_orders
VALUES(:new.Part_no,:new.Reorder_quantity,
sysdate)
ENDIF
END
()
()
ExamplesofTriggerApplications()
YoucanusetriggersinanumberofwaystocustomizeinformationmanagementinOracleDatabase.Forexample,triggersare
commonlyusedto:
Providesophisticatedauditing
Preventinvalidtransactions
Enforcereferentialintegrity(eitherthoseactionsnotsupportedbydeclarativeintegrityconstraintsoracrossnodesinadistributed
database)
Enforcecomplexbusinessrules
Enforcecomplexsecurityauthorizations
Providetransparenteventlogging
Automaticallygeneratederivedcolumnvalues
Enablebuildingcomplexviewsthatareupdatable
Tracksystemevents
Thissectionprovidesanexampleofeachofthesetriggerapplications.Theseexamplesarenotmeanttobeusedexactlyaswritten:
Theyareprovidedtoassistyouindesigningyourowntriggers.
()AuditingwithTriggers:Example()()
TriggersarecommonlyusedtosupplementthebuiltinauditingfeaturesofOracleDatabase.Althoughtriggerscanbewrittento
recordinformationsimilartothatrecordedbythe AUDITstatement,triggersshouldbeusedonlywhenmoredetailedaudit
informationisrequired.Forexample,usetriggerstoprovidevaluebasedauditingforeachrow.
Sometimes,the AUDITstatementisconsideredasecurityauditfacility,whiletriggerscanprovidefinancialauditfacility.
Whendecidingwhethertocreateatriggertoauditdatabaseactivity,considerwhatOracleDatabase'sauditingfeaturesprovide,
comparedtoauditingdefinedbytriggers,asshowninTable91.
()()Table91ComparisonofBuiltinAuditingandTriggerBasedAuditing
AuditFeature
Description
DMLandDDLAuditing
Standardauditingoptionspermit
auditingofDMLandDDLstatements
regardingalltypesofschemaobjects
andstructures.Comparatively,triggers
permitauditingofDMLstatements
enteredagainsttables,andDDL
auditingat SCHEMAor DATABASElevel.
CentralizedAuditTrail
Alldatabaseauditinformationis
recordedcentrallyandautomatically
usingtheauditingfeaturesofOracle
Database.
DeclarativeMethod
Auditingfeaturesenabledusingthe
standardOracleDatabasefeaturesare
easiertodeclareandmaintain,andless
pronetoerrors,whencomparedto
auditingfunctionsdefinedbytriggers.
AuditingOptionscanbeAudited
Anychangestoexistingauditing
optionscanalsobeauditedtoguard
againstmaliciousdatabaseactivity.
SessionandExecutiontimeAuditing
Usingthedatabaseauditingfeatures,
recordscanbegeneratedonceevery
timeanauditedstatementisentered
( BY ACCESS)oronceforeverysession
thatentersanauditedstatement( BY
SESSION).Triggerscannotauditby
sessionanauditrecordisgenerated
eachtimeatriggerauditedtableis
referenced.
AuditingofUnsuccessfulDataAccess
Databaseauditingcanbesettoaudit
whenunsuccessfuldataaccessoccurs.
However,unlessautonomous
transactionsareused,anyaudit
informationgeneratedbyatriggeris
rolledbackifthetriggeringstatementis
rolledback.Formoreinformationon
autonomoustransactions,referto
OracleDatabaseConcepts
(../../server.102/b14220/toc.htm).
SessionscanbeAudited
Connectionsanddisconnections,as
wellassessionactivity(physicalI/Os,
logicalI/Os,deadlocks,andsoon),can
berecordedusingstandarddatabase
auditing.
informationisrecordedafterthetriggeringstatementissubjectedtoanyapplicableintegrityconstraints,preventingcaseswherethe
auditprocessingiscarriedoutunnecessarilyforstatementsthatgenerateexceptionstointegrityconstraints.
Choosingbetween AFTERrowand AFTERstatementtriggersdependsontheinformationbeingaudited.Forexample,rowtriggers
providevaluebasedauditingforeachtablerow.Triggerscanalsorequiretheusertosupplya"reasoncode"forissuingtheaudited
SQLstatement,whichcanbeusefulinbothrowandstatementlevelauditingsituations.
()Thefollowingexampledemonstratesatriggerthatauditsmodificationstothe Emp_tabtableforeachrow.Itrequiresthata
"reasoncode"bestoredinaglobalpackagevariablebeforetheupdate.Thisshowshowtriggerscanbeusedtoprovidevalue
basedauditingandhowtousepublicpackagevariables.
Note:
Youmayneedtosetupthefollowingdatastructuresfortheexamplestowork:
CREATEORREPLACEPACKAGEAuditpackageAS
ReasonVARCHAR2(10)
PROCEDURESet_reason(ReasonVARCHAR2)
END
CREATETABLEEmp99(
EmpnoNOTNULLNUMBER(4),
EnameVARCHAR2(10),
JobVARCHAR2(9),
MgrNUMBER(4),
HiredateDATE,
SalNUMBER(7,2),
CommNUMBER(7,2),
DeptnoNUMBER(2),
BonusNUMBER,
SsnNUMBER,
Job_classificationNUMBER)
CREATETABLEAudit_employee(
OldssnNUMBER,
OldnameVARCHAR2(10),
OldjobVARCHAR2(2),
OldsalNUMBER,
NewssnNUMBER,
NewnameVARCHAR2(10),
NewjobVARCHAR2(2),
NewsalNUMBER,
ReasonVARCHAR2(10),
User1VARCHAR2(10),
SystemdateDATE)
CREATEORREPLACETRIGGERAudit_employee
AFTERINSERTORDELETEORUPDATEONEmp99
FOREACHROW
BEGIN
/*AUDITPACKAGEisapackagewithapublicpackage
variableREASON.REASONcouldbesetbythe
applicationbyacommandsuchasEXECUTE
AUDITPACKAGE.SET_REASON(reason_string).Notethata
packagevariablehasstateforthedurationofa
sessionandthateachsessionhasaseparatecopyof
allpackagevariables.*/
IFAuditpackage.ReasonISNULLTHEN
Raise_application_error(20201,'Mustspecifyreason'
||'withAUDITPACKAGE.SET_REASON(Reason_string)')
ENDIF
/*IftheprecedingconditionalevaluatestoTRUE,the
userspecifiederrornumberandmessageisraised,
thetriggerstopsexecution,andtheeffectsofthe
triggeringstatementarerolledback.Otherwise,a
newrowisinsertedintothepredefinedauditing
tablenamedAUDIT_EMPLOYEEcontainingtheexisting
andnewvaluesoftheEmp_tabtableandthereasoncode
definedbytheREASONvariableofAUDITPACKAGE.Note
thatthe"old"valuesareNULLiftriggering
statementisanINSERTandthe"new"valuesareNULL
ifthetriggeringstatementisaDELETE.*/
INSERTINTOAudit_employeeVALUES
(:old.Ssn,:old.Ename,:old.Job_classification,:old.Sal,
:new.Ssn,:new.Ename,:new.Job_classification,:new.Sal,
auditpackage.Reason,User,Sysdate)
END
Optionally,youcanalsosetthereasoncodebackto NULLifyouwantedtoforcethereasoncodetobesetforeveryupdate.The
followingsimple AFTERstatementtriggersetsthereasoncodebackto NULLafterthetriggeringstatementisrun:
CREATEORREPLACETRIGGERAudit_employee_reset
AFTERINSERTORDELETEORUPDATEONEmp_tab
BEGIN
auditpackage.set_reason(NULL)
END
()()NoticethattheprevioustwotriggersarebothfiredbythesametypeofSQLstatement.However,the AFTERrowtriggerisfired
onceforeachrowofthetableaffectedbythetriggeringstatement,whilethe AFTERstatementtriggerisfiredonlyonceafterthe
triggeringstatementexecutioniscompleted.
Thisnexttriggeralsousestriggerstodoauditing.Ittrackschangesmadetothe Emp_tabtableandstoresthisinformationin
AUDIT_TABLEand AUDIT_TABLE_VALUES.
Note:
Youmayneedtosetupthefollowingdatastructuresfortheexampletowork:
CREATETABLEAudit_table(
SeqNUMBER,
User_atVARCHAR2(10),
Time_nowDATE,
TermVARCHAR2(10),
JobVARCHAR2(10),
ProcVARCHAR2(10),
enumNUMBER)
CREATESEQUENCEAudit_seq
CREATETABLEAudit_table_values(
SeqNUMBER,
DeptNUMBER,
Dept1NUMBER,
Dept2NUMBER)
CREATEORREPLACETRIGGERAudit_emp
AFTERINSERTORUPDATEORDELETEONEmp_tab
FOREACHROW
DECLARE
Time_nowDATE
TerminalCHAR(10)
BEGIN
getcurrenttime,andtheterminaloftheuser:
Time_now:=SYSDATE
Terminal:=USERENV('TERMINAL')
recordnewemployeeprimarykey
IFINSERTINGTHEN
INSERTINTOAudit_table
VALUES(Audit_seq.NEXTVAL,User,Time_now,
Terminal,'Emp_tab','INSERT',:new.Empno)
recordprimarykeyofthedeletedrow:
ELSIFDELETINGTHEN
INSERTINTOAudit_table
VALUES(Audit_seq.NEXTVAL,User,Time_now,
Terminal,'Emp_tab','DELETE',:old.Empno)
forupdates,recordtheprimarykey
oftherowbeingupdated:
ELSE
INSERTINTOAudit_table
VALUES(audit_seq.NEXTVAL,User,Time_now,
Terminal,'Emp_tab','UPDATE',:old.Empno)
andforSALandDEPTNO,recordoldandnewvalues:
IFUPDATING('SAL')THEN
INSERTINTOAudit_table_values
VALUES(Audit_seq.CURRVAL,'SAL',
:old.Sal,:new.Sal)
ELSIFUPDATING('DEPTNO')THEN
INSERTINTOAudit_table_values
VALUES(Audit_seq.CURRVAL,'DEPTNO',
:old.Deptno,:new.DEPTNO)
ENDIF
ENDIF
END
()IntegrityConstraintsandTriggers:Examples
()()Triggersanddeclarativeintegrityconstraintscanbothbeusedtoconstraindatainput.However,triggersandintegrityconstraints
havesignificantdifferences.
Declarativeintegrityconstraintsarestatementsaboutthedatabasethatarealwaystrue.Aconstraintappliestoexistingdatainthe
tableandanystatementthatmanipulatesthetable.
SeeAlso:
Chapter6,"MaintainingDataIntegrityinApplicationDevelopment"(adfns_constraints.htm#g1023643)
Triggersconstrainwhatatransactioncando.Atriggerdoesnotapplytodataloadedbeforethedefinitionofthetriggertherefore,it
isnotknownifalldatainatableconformstotherulesestablishedbyanassociatedtrigger.
AlthoughtriggerscanbewrittentoenforcemanyofthesamerulessupportedbyOracleDatabase'sdeclarativeintegrityconstraint
features,triggersshouldonlybeusedtoenforcecomplexbusinessrulesthatcannotbedefinedusingstandardintegrityconstraints.
ThedeclarativeintegrityconstraintfeaturesprovidedwithOracleDatabaseofferthefollowingadvantageswhencomparedto
constraintsdefinedbytriggers:
Centralizedintegritychecks.Allpointsofdataaccessmustadheretotheglobalsetofrulesdefinedbytheintegrityconstraints
correspondingtoeachschemaobject.
Declarativemethod.Constraintsdefinedusingthestandardintegrityconstraintfeaturesaremucheasiertowriteandarelessprone
toerrors,whencomparedwithcomparableconstraintsdefinedbytriggers.
Whilemostaspectsofdataintegritycanbedefinedandenforcedusingdeclarativeintegrityconstraints,triggerscanbeusedto
enforcecomplexbusinessconstraintsnotdefinableusingdeclarativeintegrityconstraints.Forexample,triggerscanbeusedto
enforce:
Referentialintegritywhentheparentandchildtablesareondifferentnodesofadistributeddatabase.
()Complexcheckconstraintsnotdefinableusingtheexpressionsallowedina CHECKconstraint.
()ReferentialIntegrityUsingTriggers()()
Therearemanycaseswherereferentialintegritycanbeenforcedusingtriggers.Note,however,youshouldonlyusetriggerswhen
thereisnodeclarativesupportfortheactionyouareperforming.
Whenusingtriggerstomaintainreferentialintegrity,declarethe PRIMARY(or UNIQUE) KEYconstraintintheparenttable.If
referentialintegrityisbeingmaintainedbetweenaparentandchildtableinthesamedatabase,thenyoucanalsodeclarethe
foreignkeyinthechildtable,butdisableitthispreventsthecorresponding PRIMARY KEYconstraintfrombeingdropped(unless
the PRIMARY KEYconstraintisexplicitlydroppedwiththe CASCADEoption).
Tomaintainreferentialintegrityusingtriggers:
Atriggermustbedefinedforthechildtablethatguaranteesvaluesinsertedorupdatedintheforeignkeycorrespondtovaluesin
theparentkey.
Oneormoretriggersmustbedefinedfortheparenttable.Thesetriggersguaranteethedesiredreferentialaction( RESTRICT,
CASCADE,or SET NULL)forvaluesintheforeignkeywhenvaluesareupdatedordeletedintheparentkey.Noactionisrequired
forinsertsintotheparenttable(nodependentforeignkeysexist).
tablerelationshipisusedintheseexamples.
Severalofthetriggersincludestatementsthatlockrows( SELECT... FOR UPDATE).Thisoperationisnecessarytomaintain
concurrencyastherowsarebeingprocessed.
()
ForeignKeyTriggerforChildTable
()Thefollowingtriggerguaranteesthatbeforean INSERTor UPDATEstatementaffectsaforeignkeyvalue,thecorrespondingvalue
existsintheparentkey.Themutatingtableexceptionincludedinthefollowingexampleallowsthistriggertobeusedwiththe
UPDATE_SET_DEFAULTand UPDATE_CASCADEtriggers.Thisexceptioncanberemovedifthistriggerisusedalone.
()CREATEORREPLACETRIGGEREmp_dept_check
BEFOREINSERTORUPDATEOFDeptnoONEmp_tab
FOREACHROWWHEN(new.DeptnoISNOTNULL)
Beforearowisinserted,orDEPTNOisupdatedintheEmp_tab
table,firethistriggertoverifythatthenewforeign
keyvalue(DEPTNO)ispresentintheDept_tabtable.
DECLARE
DummyINTEGERtobeusedforcursorfetch
Invalid_departmentEXCEPTION
Valid_departmentEXCEPTION
Mutating_tableEXCEPTION
PRAGMAEXCEPTION_INIT(Mutating_table,4091)
Cursorusedtoverifyparentkeyvalueexists.If
present,lockparentkey'srowsoitcan'tbe
deletedbyanothertransactionuntilthis
transactioniscommittedorrolledback.
CURSORDummy_cursor(DnNUMBER)IS
SELECTDeptnoFROMDept_tab
WHEREDeptno=Dn
FORUPDATEOFDeptno
BEGIN
OPENDummy_cursor(:new.Deptno)
FETCHDummy_cursorINTODummy
Verifyparentkey.Ifnotfound,raiseuserspecified
errornumberandmessage.Iffound,closecursor
beforeallowingtriggeringstatementtocomplete:
IFDummy_cursor%NOTFOUNDTHEN
RAISEInvalid_department
ELSE
RAISEvalid_department
ENDIF
CLOSEDummy_cursor
EXCEPTION
WHENInvalid_departmentTHEN
CLOSEDummy_cursor
Raise_application_error(20000,'InvalidDepartment'
||'Number'||TO_CHAR(:new.deptno))
WHENValid_departmentTHEN
CLOSEDummy_cursor
WHENMutating_tableTHEN
NULL
END
()
UPDATEandDELETERESTRICTTriggerforParentTable()()
Thefollowingtriggerisdefinedonthe DEPT_TABtabletoenforcethe UPDATEand DELETE RESTRICTreferentialactiononthe
primarykeyofthe DEPT_TABtable:
CREATEORREPLACETRIGGERDept_restrict
BEFOREDELETEORUPDATEOFDeptnoONDept_tab
FOREACHROW
BeforearowisdeletedfromDept_tabortheprimarykey
(DEPTNO)ofDept_tabisupdated,checkfordependent
foreignkeyvaluesinEmp_tabrollbackifanyarefound.
DECLARE
DummyINTEGERtobeusedforcursorfetch
Employees_presentEXCEPTION
employees_not_presentEXCEPTION
Cursorusedtocheckfordependentforeignkeyvalues.
CURSORDummy_cursor(DnNUMBER)IS
SELECTDeptnoFROMEmp_tabWHEREDeptno=Dn
BEGIN
OPENDummy_cursor(:old.Deptno)
FETCHDummy_cursorINTODummy
Ifdependentforeignkeyisfound,raiseuserspecified
errornumberandmessage.Ifnotfound,closecursor
beforeallowingtriggeringstatementtocomplete.
IFDummy_cursor%FOUNDTHEN
RAISEEmployees_presentdependentrowsexist
ELSE
RAISEEmployees_not_presentnodependentrows
ENDIF
CLOSEDummy_cursor
EXCEPTION
WHENEmployees_presentTHEN
CLOSEDummy_cursor
Raise_application_error(20001,'EmployeesPresentin'
||'Department'||TO_CHAR(:old.DEPTNO))
WHENEmployees_not_presentTHEN
CLOSEDummy_cursor
END
Caution:
Thistriggerdoesnotworkwithselfreferentialtables(tableswithboththeprimary/uniquekeyandtheforeignkey).Also,
thistriggerdoesnotallowtriggerstocycle(suchas,AfiresBfiresA).()
()
UPDATEandDELETESETNULLTriggersforParentTable:Example()()
Thefollowingtriggerisdefinedonthe DEPT_TABtabletoenforcethe UPDATEand DELETE SET NULLreferentialactiononthe
primarykeyofthe DEPT_TABtable:
CREATEORREPLACETRIGGERDept_set_null
AFTERDELETEORUPDATEOFDeptnoONDept_tab
FOREACHROW
BeforearowisdeletedfromDept_tabortheprimarykey
(DEPTNO)ofDept_tabisupdated,setallcorresponding
dependentforeignkeyvaluesinEmp_tabtoNULL:
BEGIN
IFUPDATINGAND:OLD.Deptno!=:NEW.DeptnoORDELETINGTHEN
UPDATEEmp_tabSETEmp_tab.Deptno=NULL
WHEREEmp_tab.Deptno=:old.Deptno
ENDIF
END
()
DELETECascadeTriggerforParentTable:Example
Thefollowingtriggeronthe DEPT_TABtableenforcesthe DELETE CASCADEreferentialactionontheprimarykeyofthe DEPT_TAB
table:
CREATEORREPLACETRIGGERDept_del_cascade
AFTERDELETEONDept_tab
FOREACHROW
BeforearowisdeletedfromDept_tab,deleteall
rowsfromtheEmp_tabtablewhoseDEPTNOisthesameas
theDEPTNObeingdeletedfromtheDept_tabtable:
BEGIN
DELETEFROMEmp_tab
WHEREEmp_tab.Deptno=:old.Deptno
END
Note:
Typically,thecodefor DELETE CASCADE iscombinedwiththecodefor UPDATE SET NULL or UPDATE SET
DEFAULT toaccountforbothupdatesanddeletes.
()
UPDATECascadeTriggerforParentTable:Example
()()Thefollowingtriggerensuresthatifadepartmentnumberisupdatedinthe Dept_tabtable,thenthischangeispropagatedto
dependentforeignkeysinthe Emp_tabtable:
Generateasequencenumbertobeusedasaflagfor
determiningifanupdatehasoccurredonacolumn:
CREATESEQUENCEUpdate_sequence
INCREMENTBY1MAXVALUE5000
CYCLE
CREATEORREPLACEPACKAGEIntegritypackageAS
UpdateseqNUMBER
ENDIntegritypackage
CREATEORREPLACEPACKAGEBODYIntegritypackageAS
ENDIntegritypackage
createflagcol:
ALTERTABLEEmp_tabADDUpdate_idNUMBER
CREATEORREPLACETRIGGERDept_cascade1BEFOREUPDATEOFDeptnoONDept_tab
DECLARE
DummyNUMBER
BeforeupdatingtheDept_tabtable(thisisastatement
trigger),generateanewsequencenumberandassign
ittothepublicvariableUPDATESEQofauserdefined
packagenamedINTEGRITYPACKAGE:
BEGIN
SELECTUpdate_sequence.NEXTVAL
INTODummy
FROMdual
Integritypackage.Updateseq:=Dummy
END
CREATEORREPLACETRIGGERDept_cascade2AFTERDELETEORUPDATE
OFDeptnoONDept_tabFOREACHROW
ForeachdepartmentnumberinDept_tabthatisupdated,
cascadetheupdatetodependentforeignkeysinthe
Emp_tabtable.Onlycascadetheupdateifthechildrow
hasnotalreadybeenupdatedbythistrigger:
BEGIN
IFUPDATINGTHEN
UPDATEEmp_tab
SETDeptno=:new.Deptno,
Update_id=Integritypackage.Updateseqfrom1st
WHEREEmp_tab.Deptno=:old.Deptno
ANDUpdate_idISNULL
/*onlyNULLifnotupdatedbythe3rdtrigger
firedbythissametriggeringstatement*/
ENDIF
IFDELETINGTHEN
BeforearowisdeletedfromDept_tab,deleteall
rowsfromtheEmp_tabtablewhoseDEPTNOisthesameas
theDEPTNObeingdeletedfromtheDept_tabtable:
DELETEFROMEmp_tab
WHEREEmp_tab.Deptno=:old.Deptno
ENDIF
END
CREATEORREPLACETRIGGERDept_cascade3AFTERUPDATEOFDeptnoONDept_tab
BEGINUPDATEEmp_tab
SETUpdate_id=NULL
WHEREUpdate_id=Integritypackage.Updateseq
END
Note:
Becausethistriggerupdatesthe Emp_tab table,the Emp_dept_check trigger,ifenabled,isalsofired.Theresulting
mutatingtableerroristrappedbythe Emp_dept_check trigger.Youshouldcarefullytestanytriggersthatrequireerror
trappingtosucceedtoensurethattheyalwaysworkproperlyinyourenvironment.
()TriggerforComplexCheckConstraints:Example()()
()Triggerscanenforceintegrityrulesotherthanreferentialintegrity.Forexample,thistriggerperformsacomplexcheckbefore
allowingthetriggeringstatementtorun.
Note:
Youmayneedtosetupthefollowingdatastructuresfortheexampletowork:
CREATETABLESalgrade(
GradeNUMBER,
LosalNUMBER,
HisalNUMBER,
Job_classificationNUMBER)
CREATEORREPLACETRIGGERSalary_check
BEFOREINSERTORUPDATEOFSal,JobONEmp99
FOREACHROW
DECLARE
MinsalNUMBER
MaxsalNUMBER
Salary_out_of_rangeEXCEPTION
BEGIN
/*Retrievetheminimumandmaximumsalaryforthe
employee'snewjobclassificationfromtheSALGRADE
tableintoMINSALandMAXSAL:*/
SELECTMinsal,MaxsalINTOMinsal,MaxsalFROMSalgrade
WHEREJob_classification=:new.Job
/*Iftheemployee'snewsalaryislessthanorgreater
thanthejobclassification'slimits,theexceptionis
raised.Theexceptionmessageisreturnedandthe
pendingINSERTorUPDATEstatementthatfiredthe
triggerisrolledback:*/
IF(:new.Sal<MinsalOR:new.Sal>Maxsal)THEN
RAISESalary_out_of_range
ENDIF
EXCEPTION
WHENSalary_out_of_rangeTHEN
Raise_application_error(20300,
'Salary'||TO_CHAR(:new.Sal)||'outofrangefor'
||'jobclassification'||:new.Job
||'foremployee'||:new.Ename)
WHENNO_DATA_FOUNDTHEN
Raise_application_error(20322,
'InvalidJobClassification'
||:new.Job_classification)
END
()ComplexSecurityAuthorizationsandTriggers():Example
Triggersarecommonlyusedtoenforcecomplexsecurityauthorizationsfortabledata.Onlyusetriggerstoenforcecomplexsecurity
authorizationsthatcannotbedefinedusingthedatabasesecurityfeaturesprovidedwithOracleDatabase.Forexample,atrigger
canprohibitupdatestosalarydataofthe Emp_tabtableduringweekends,holidays,andnonworkinghours.
()()()Whenusingatriggertoenforceacomplexsecurityauthorization,itisbesttousea BEFOREstatementtrigger.Usinga BEFORE
statementtriggerhasthesebenefits:
Thesecuritycheckisdonebeforethetriggeringstatementisallowedtorun,sothatnowastedworkisdonebyanunauthorized
statement.
Thesecuritycheckisperformedonlyonceforthetriggeringstatement,notforeachrowaffectedbythetriggeringstatement.
()Thisexampleshowsatriggerusedtoenforcesecurity.
Note:
Youmayneedtosetupthefollowingdatastructuresfortheexampletowork:
CREATETABLECompany_holidays(DayDATE)
CREATEORREPLACETRIGGEREmp_permit_changes
BEFOREINSERTORDELETEORUPDATEONEmp99
DECLARE
DummyINTEGER
Not_on_weekendsEXCEPTION
Not_on_holidaysEXCEPTION
Non_working_hoursEXCEPTION
BEGIN
/*checkforweekends:*/
IF(TO_CHAR(Sysdate,'DY')='SAT'OR
TO_CHAR(Sysdate,'DY')='SUN')THEN
RAISENot_on_weekends
ENDIF
/*checkforcompanyholidays:*/
SELECTCOUNT(*)INTODummyFROMCompany_holidays
WHERETRUNC(Day)=TRUNC(Sysdate)
/*TRUNCgetsridoftimepartsofdates:*/
IFdummy>0THEN
RAISENot_on_holidays
ENDIF
/*Checkforworkhours(8amto6pm):*/
IF(TO_CHAR(Sysdate,'HH24')<8OR
TO_CHAR(Sysdate,'HH24')>18)THEN
RAISENon_working_hours
ENDIF
EXCEPTION
WHENNot_on_weekendsTHEN
Raise_application_error(20324,'Maynotchange'
||'employeetableduringtheweekend')
WHENNot_on_holidaysTHEN
Raise_application_error(20325,'Maynotchange'
||'employeetableduringaholiday')
WHENNon_working_hoursTHEN
Raise_application_error(20326,'Maynotchange'
||'Emp_tabtableduringnonworkinghours')
END
SeeAlso:
OracleDatabaseSecurityGuide(../../network.102/b14266/apdvntro.htm#DBSEG12000)fordetailsondatabasesecurityfeatures
()TransparentEventLoggingandTriggers
Triggersareveryusefulwhenyouwanttotransparentlyperformarelatedchangeinthedatabasefollowingcertainevents.
The REORDERtriggerexampleshowsatriggerthatreorderspartsasnecessarywhencertainconditionsaremet.(Inotherwords,a
triggeringstatementisentered,andthe PARTS_ON_HANDvalueislessthanthe REORDER_POINTvalue.)
()DerivedColumnValuesandTriggers:Example()()
()()Triggerscanderivecolumnvaluesautomatically,baseduponavalueprovidedbyan INSERTor UPDATEstatement.Thistypeof
triggerisusefultoforcevaluesinspecificcolumnsthatdependonthevaluesofothercolumnsinthesamerow. BEFORErow
triggersarenecessarytocompletethistypeofoperationforthefollowingreasons:
()Thefollowingexampleillustrateshowatriggercanbeusedtoderivenewcolumnvaluesforatablewheneverarowisinsertedor
updated.
Note:
Youmayneedtosetupthefollowingdatastructuresfortheexampletowork:
ALTERTABLEEmp99ADD(
UppernameVARCHAR2(20),
SoundexnameVARCHAR2(20))
CREATEORREPLACETRIGGERDerived
BEFOREINSERTORUPDATEOFEnameONEmp99
/*BeforeupdatingtheENAMEfield,derivethevaluesfor
theUPPERNAMEandSOUNDEXNAMEfields.Usersshouldbe
restrictedfromupdatingthesefieldsdirectly:*/
FOREACHROW
BEGIN
:new.Uppername:=UPPER(:new.Ename)
:new.Soundexname:=SOUNDEX(:new.Ename)
END
()BuildingComplexUpdatableViewsUsingTriggers:Example
Viewsareanexcellentmechanismtoprovidelogicalwindowsovertabledata.However,whentheviewquerygetscomplex,the
systemimplicitlycannottranslatetheDMLontheviewintothoseontheunderlyingtables. INSTEAD OFtriggershelpsolvethis
problem.Thesetriggerscanbedefinedoverviews,andtheyfireinsteadoftheactualDML.
Consideralibrarysystemwherebooksarearrangedundertheirrespectivetitles.Thelibraryconsistsofacollectionofbooktype
objects.Thefollowingexampleexplainstheschema.
CREATEORREPLACETYPEBook_tASOBJECT
(
BooknumNUMBER,
TitleVARCHAR2(20),
AuthorVARCHAR2(20),
AvailableCHAR(1)
)
CREATEORREPLACETYPEBook_list_tASTABLEOFBook_t
Assumethatthefollowingtablesexistintherelationalschema:
TableBook_table(Booknum,Section,Title,Author,Available)
Booknum
Section
Title
Author
Available
121001
Classic
Iliad
Homer
121002
Novel
GoneWiththeWind
MitchellM
Youcandefineacomplexviewoverthesetablestocreatealogicalviewofthelibrarywithsectionsandacollectionofbooksin
eachsection.
CREATEORREPLACEVIEWLibrary_viewAS
SELECTi.Section,CAST(MULTISET(
SELECTb.Booknum,b.Title,b.Author,b.Available
FROMBook_tableb
WHEREb.Section=i.Section)ASBook_list_t)BOOKLIST
FROMLibrary_tablei
CREATEORREPLACETRIGGERLibrary_triggerINSTEADOFINSERTONLibrary_viewFOREACHROW
BookvarBOOK_T
iINTEGER
BEGIN
INSERTINTOLibrary_tableVALUES(:NEW.Section)
FORiIN1..:NEW.Booklist.COUNTLOOP
Bookvar:=Booklist(i)
INSERTINTObook_table
VALUES(Bookvar.booknum,:NEW.Section,Bookvar.Title,Bookvar.Author,bookvar.Available)
ENDLOOP
END
/
Similarly,youcanalsodefinetriggersonthenestedtable booklisttohandlemodificationofthenestedtableelement.
()TrackingSystemEventsUsingTriggers()()()
()
FineGrainedAccessControlUsingTriggers:Example
Systemtriggerscanbeusedtosetapplicationcontext.Applicationcontextisarelativelynewfeaturethatenhancesyourabilityto
implementfinegrainedaccesscontrol.Applicationcontextisasecuresessioncache,anditcanbeusedtostoresessionspecific
attributes.
Intheexamplethatfollows,procedure set_ctxsetstheapplicationcontextbasedontheuserprofile.Thetrigger setexpensectx
ensuresthatthecontextissetforeveryuser.
CONNECTsecdemo/secdemo
CREATEORREPLACECONTEXTExpenses_reportingUSINGSecdemo.Exprep_ctx
REM=================================================================
REMCreationofthepackagewhichimplementsthecontext:
REM=================================================================
CREATEORREPLACEPACKAGEExprep_ctxAS
PROCEDURESet_ctx
END
SHOWERRORS
CREATEORREPLACEPACKAGEBODYExprep_ctxIS
PROCEDURESet_ctxIS
EmpnumNUMBER
CountrecNUMBER
CcNUMBER
RoleVARCHAR2(20)
BEGIN
SETemp_number:
SELECTEmployee_idINTOEmpnumFROMEmployee
WHERELast_name=SYS_CONTEXT('userenv','session_user')
DBMS_SESSION.SET_CONTEXT('expenses_reporting','emp_number',Empnum)
SETROLE:
SELECTCOUNT(*)INTOCountrecFROMCost_centerWHEREManager_id=Empnum
IF(countrec>0)THEN
DBMS_SESSION.SET_CONTEXT('expenses_reporting','exp_role','MANAGER')
ELSE
DBMS_SESSION.SET_CONTEXT('expenses_reporting','exp_role','EMPLOYEE')
ENDIF
SETcc_number:
SELECTCost_center_idINTOCcFROMEmployee
WHERELast_name=SYS_CONTEXT('userenv','session_user')
DBMS_SESSION.SET_CONTEXT(expenses_reporting','cc_number',Cc)
END
END
()
CALLSyntax
CREATEORREPLACETRIGGERSecdemo.Setexpseetx
AFTERLOGONONDATABASE
CALLSecdemo.Exprep_etx.Set_otx
()
()
Respondingto()SystemEventsthroughTriggers()
Systemeventpublicationletsapplicationssubscribetodatabaseevents,justliketheysubscribetomessagesfromother
applications.Thesystemeventspublicationframeworkincludesthefollowingfeatures:
Infrastructureforpublish/subscribe,bymakingthedatabaseanactivepublisherofevents.
Integrationofdatacartridgesintheserver.Thesystemeventspublicationcanbeusedtonotifycartridgesofstatechangesinthe
server.
Integrationoffinegrainedaccesscontrolintheserver.
Bycreatingatrigger,youcanspecifyaprocedurethatrunswhenaneventoccurs.DMLeventsaresupportedontables,and
systemeventsaresupportedon DATABASEand SCHEMA.Youcanturnnotificationonandoffbyenablinganddisablingthetrigger
usingthe ALTERTRIGGERstatement.
ThisfeatureisintegratedwiththeAdvancedQueueingengine.Publish/subscribeapplicationsusethe DBMS_AQ. ENQUEUE()
procedure,andotherapplicationssuchascartridgesusecallouts.
SeeAlso:
OracleDatabaseSQLReference(../../server.102/b14200/toc.htm)
OracleStreamsAdvancedQueuingUser'sGuideandReference(../../server.102/b14257/toc.htm)fordetailsonhowto
subscribetopublishedevents
()
HowEventsArePublishedThroughTriggers
Wheneventsaredetectedbythedatabase,thetriggermechanismexecutestheactionspecifiedinthetrigger.Aspartofthisaction,
youcanusethe DBMS_AQpackagetopublishtheeventtoaqueuesothatsubscribersreceivenotifications.
Note:
Onlysystemdefineddatabaseeventscanbedetectedthisway.Youcannotdefineyourowneventconditions.
Whenaneventoccurs,thedatabasefiresalltriggersthatareenabledonthatevent,withsomeexceptions:
Ifthetriggerisactuallythetargetofthetriggeringevent,itisnotfired.Forexample,atriggerforall DROPeventsisnotfiredwhen
itisdroppeditself.
Ifatriggerhasbeenmodifiedbutnotcommittedwithinthesametransactionasthefiringevent.Forexample,recursiveDDL
withinasystemtriggermightmodifyatrigger,whichpreventsthemodifiedtriggerfrombeingfiredbyeventswithinthesame
transaction.
Youcancreatemorethanonetriggeronanobject.Whenaneventfiresmorethanonetrigger,thefiringorderisnotdefinedandso
youshouldnotrelyonthetriggersbeingfiredinaparticularorder.
()
PublicationContext
Whenaneventispublished,certainruntimecontextandattributes,asspecifiedintheparameterlist,arepassedtothecallout
procedure.Asetoffunctionscalledeventattributefunctionsareprovided.
SeeAlso:
"EventAttributeFunctions"forinformationoneventspecificattributes
Foreachsupportedsystemevent,youcanidentifyandpredefineeventspecificattributesfortheevent.Youcanchoosethe
parameterlisttobeanyoftheseattributes,alongwithothersimpleexpressions.Forcallouts,thesearepassedas INarguments.
()
ErrorHandling
Returnstatusfrompublicationcalloutfunctionsforalleventsareignored.Forexample,with SHUTDOWNevents,thedatabasecannot
doanythingwiththereturnstatus.
SeeAlso:
"ListofDatabaseEvents"fordetailsonreturnstatus
()
ExecutionModel
()Traditionally,triggersexecuteasthedefinerofthetrigger.Thetriggeractionofaneventisexecutedasthedefineroftheaction
(asthedefinerofthepackageorfunctionincallouts,orasownerofthetriggerinqueues).Becausetheownerofthetriggermust
have EXECUTEprivilegesontheunderlyingqueues,packages,orprocedure,thisbehaviorisconsistent.
()
()
EventAttributeFunctions()()()
Whenthedatabasefiresatrigger,youcanretrievecertainattributesabouttheeventthatfiredthetrigger.Youcanretrieveeach
attributewithafunctioncall.Table92describesthesystemdefinedeventattributes.
Note:
Tomaketheseattributesavailable,youmustfirstrunthe C()ATPROC.SQLscript.
Thetriggerdictionaryobjectmaintainsmetadataabouteventsthatwillbepublishedandtheircorrespondingattributes.
Inearlierreleases,thesefunctionswereaccessedthroughthe SYSpackage.Werecommendyouusethesepublic
synonymswhosenamesbeginwith ora_.
()()Table92SystemDefinedEventAttributes
Attribute
ora_client_ip_address
ora_database_name
Type
VARCHAR2
VARCHAR2(50)
Description
ReturnsIP
addressofthe
clientina
LOGONevent
whenthe
underlying
protocolis
TCP/IP
Database
name.
Example
DECLARE
v_addrVARCHAR
IF(ora_syseve
v_addr:=or
ENDIF
END
DECLARE
v_db_nameVARC
BEGIN
v_db_name:=o
END
ora_des_encrypted_password
ora_dict_obj_name
ora_dict_obj_name_list
VARCHAR2
VARCHAR(30)
BINARY_INTEGER
(name_listOUTora_name_list_t)
ora_dict_obj_ow()()()()()()ner()()()()()()
ora_dict_obj_owner_list
(owner_listOUTora_name_list_t)
VARCHAR(30)
BINARY_INTEGER
TheDES
encrypted
passwordofthe
userbeing
createdor
altered.
Nameofthe
dictionaryobject
onwhichthe
DDLoperation
occurred.
Returnthelistof
objectnamesof
objectsbeing
modifiedinthe
event.
Ownerofthe
dictionaryobject
onwhichthe
DDLoperation
occurred.
Returnsthelist
ofobjectowners
ofobjectsbeing
IF(ora_dict_obj
INSERTINTOev
VALUES(ora_
ENDIF
INSERTINTOeven
VALUES('Chang
ora_d
IF(ora_sysevent
THENnumber_mo
ora_dict_
ENDIF
INSERTINTOeven
VALUES('objec
ora_d
modifiedinthe
event.
IF(ora_sysevent
THENnumber_of
ora_dict_ob
ENDIF
ora_dict_obj_type
ora_grantee
VARCHAR(20)
BINARY_INTEGER
(user_listOUTora_name_list_t)
ora_instance_num
NUMBER
Typeofthe
dictionaryobject
onwhichthe
DDLoperation
occurred.
Returnsthe
granteesofa
granteventin
theOUT
parameter
returnsthe
numberof
granteesinthe
returnvalue.
Instance
number.
INSERTINTOeven
VALUES('This
ora_d
IF(ora_sysevent
number_of_user
ENDIF
IF(ora_instance
INSERTINTOev
ENDIF
ora_is_alter_column
BOOLEAN
(column_nameINVARCHAR2)
ora_is_creating_nested_table()
BOOLEAN
Returnstrueif
thespecified
columnis
altered.
Returnstrueif
thecurrent
eventiscreating
anestedtable
IF(ora_sysevent
ora_dict_obj
alter_column:=
ENDIF
IF(ora_sysevent
ora_dict_obj
ora_is_creat
INSERTINTOev
VALUES('An
ENDIF
ora_is_drop_column
BOOLEAN
(column_nameINVARCHAR2()()()()()()())
ora_is_servererror
BOOLEAN
Returnstrueif
thespecified
columnis
dropped.
Returns TRUEif
givenerrorison
errorstack,
FALSE
otherwise.
ora_login_user
ora_partition_pos
VARCHAR2(30)
BINARY_INTEGER
IF(ora_sysevent
ora_dict_obj
drop_column:=
ENDIF
IF(ora_is_serve
INSERTINTOeve
VALUES('Serv
ENDIF
Loginuser
name.
SELECTora_login
Inan
Retrieveora_
INSTEADOF
triggerfor
CREATETABLE,
theposition
withintheSQL
textwhereyou
couldinserta
PARTITION
clause.
FROMdual
sql_textvari
v_n:=ora_parti
v_new_stmt:=SU
||''
||''
ora_privilege_list
BINARY_INTEGER
(privilege_list
OUTora_name_list_t)
ora_revokee
BINARY_INTEGER
(user_listOUTora_name_list_t)
Returnsthelist
ofprivileges
beinggranted
bythegrantee
orthelistof
privileges
revokedfrom
therevokeesin
the OUT
parameter
returnsthe
numberof
privilegesinthe
returnvalue.
Returnsthe
revokeesofa
revokeeventin
the OUT
IF(ora_sysevent
ora_sysevent
number_of_priv
ora_privileg
ENDIF
IF(ora_sysevent
number_of_users
parameter
returnsthe
numberof
revokeesinthe
returnvalue.
ora_server_error
ora_server_error_depth
ora_server_error_msg
NUMBER
BINARY_INTEGER
VARCHAR2
(positioninbinary_integer)
ora_server_error_num_params
BINARY_INTEGER
(positioninbinary_integer)
ora_server_error_param
(positioninbinary_integer,
paraminbinary_integer)
Givenaposition
(1fortopof
stack),itreturns
theerror
numberatthat
positiononerror
stack
Returnsthe
totalnumberof
errormessages
ontheerror
stack.
Givenaposition
(1fortopof
stack),itreturns
theerror
messageatthat
positiononerror
stack
Givenaposition
( 1fortopof
INSERTINTOeven
VALUES('tops
ora_s
n:=ora_server_
Thisvalueis
INSERTINTOeven
VALUES('tops
ora_s
n:=ora_server_
stack),itreturns
thenumberof
stringsthat
havebeen
substitutedinto
theerror
messageusing
aformatlike
%s.
VARCHAR2
Givenaposition
( 1fortopof
stack)anda
parameter
number,returns
thematching
substitution
value( %s, %d,
andsoon)in
theerror
message.
Forexample,
message:"Exp
param:=ora_ser
ora_sql_txt
BINARY_INTEGER
(sql_textoutora_name_list_t)
ora_sysevent
VARCHAR2(20)
ora_with_grant_option()
BOOLEAN
Returnsthe
SQLtextofthe
triggering
statementinthe
OUTparameter.
Ifthestatement
islong,itis
brokeninto
multiplePL/SQL
tableelements.
Thefunction
returnvalue
showsthe
numberof
elementsarein
thePL/SQL
table.
Systemevent
firingthetrigger:
Eventnameis
sameasthatin
thesyntax.
Returnstrueif
theprivileges
aregrantedwith
grantoption.
sql_textora_nam
v_stmtVARCHAR2(
...
n:=ora_sql_txt
FORiIN1..nLO
v_stmt:=v_st
ENDLOOP
INSERTINTOeven
VALUES('text
||v_
INSERTINTOeven
VALUES(ora_sy
IF(ora_sysevent
ora_with_gra
INSERTINTOev
VALUES('wit
ENDIF
space_error_info
BOOLEAN
(error_numberOUTNUMBER,
error_typeOUTVARCHAR2,
object_ownerOUTVARCHAR2,
table_space_nameOUT
VARCHAR2,
object_nameOUTVARCHAR2,
sub_object_nameOUTVARCHAR2)
Returnstrueif
theerroris
relatedtoan
outofspace
condition,and
fillsinthe OUT
parameterswith
information
abouttheobject
thatcausedthe
error.
IF(space_error_
DBMS_OUTPUT.PU
'
ENDIF
()
()
()ListofDatabaseEvents
Thissectiondescribesimportantsystemeventsandclientevents.
()
SystemEvent()()()()s
Systemeventsarerelatedtoentireinstancesorschemas,notindividualtablesorrows.Triggerscreatedonstartupandshutdown
eventsmustbeassociatedwiththedatabaseinstance.Triggerscreatedonerrorandsuspendeventscanbeassociatedwitheither
thedatabaseinstanceoraparticularschema.
Table93containsalistofsystemmanagerevents.
()()Table93SystemManagerEvents
Event
STARTUP
WhenFired?
Conditions
Restrictions
Transaction
Whenthedatabaseis
opened.
None
allowed
Nodatabase
operations
allowedin
thetrigger.
Startsa
separate
transaction
andcommits
itafterfiring
thetriggers.
Return
AttributeFunctions
ora_sysevent
ora_login_user
ora_instance_num
ora_database_name
status
ignored.
SHUTDOWN
Justbeforetheserver
startstheshutdownofan
instance.
None
allowed
Thisletsthecartridge
shutdowncompletely.For
abnormalinstance
shutdown,thiseventmay
notbefired.
()DB_ROLE_CHANGE
SERVERERROR
Whenthedatabaseis
openedforthefirsttime
afterarolechange.
Nodatabase
operations
allowedin
thetrigger.
Return
status
ignored.
None
allowed
Whentheerror eno
ERRNO=
occurs.Ifnoconditionis
given,thenthiseventfires
wheneveranerroroccurs.
eno
Thetriggerdoesnotfire
on ORA1034,
Return
status
ignored.
Dependson
theerror.
Return
status
ignored.
Startsa
separate
transaction
andcommits
itafterfiring
thetriggers.
Startsa
separate
transaction
andcommits
itafterfiring
thetriggers.
Startsa
separate
transaction
andcommits
itafterfiring
thetriggers.
ora_sysevent
ora_login_user
ora_instance_num
ora_database_name
ora_sysevent
ora_login_user
ora_instance_num
ora_database_name
ora_sysevent
ora_login_user
ora_instance_num
ora_database_name
ora_server_error
ora_is_servererror
space_error_info
ORA1403, ORA1422,
ORA1423,and
ORA4030becausethey
arenottrueerrorsorare
tooserioustocontinue
processing.Italsofailsto
fireon ORA18and
ORA20becausea
processisnotavailableto
connecttothedatabase
torecordtheerror.
()
ClientEvents()()()()
Clienteventsaretheeventsrelatedtouserlogon/logoff,DML,andDDLoperations.Forexample:
CREATEORREPLACETRIGGEROn_Logon
AFTERLOGON
ONThe_user.Schema
BEGIN
Do_Something
END
Event
WhenFired?
AttributeFunctions
BEFOREALTER
Whenacatalogobjectisaltered.
ora_sysevent
ora_login_user
AFTERALTER
ora_instance_num
ora_database_name
ora_dict_obj_type
ora_dict_obj_name
ora_dict_obj_owner
ora_des_encrypted_password
(forALTERUSERevents)
ora_is_alter_column
(forALTERTABLEevents)
ora_is_drop_column
(forALTERTABLEevents)
BEFOREDROP
Whenacatalogobjectisdropped.
ora_sysevent
ora_login_user
AFTERDROP
ora_instance_num
ora_database_name
ora_dict_obj_type
ora_dict_obj_name
ora_dict_obj_owner
BEFOREANALYZE
Whenananalyzestatementisissued
ora_sysevent
ora_login_user
AFTERANALYZE
ora_instance_num
ora_database_name
ora_dict_obj_name
ora_dict_obj_type
ora_dict_obj_owner
BEFOREASSOCIATESTATISTICS
Whenanassociatestatisticsstatementis
issued
AFTERASSOCIATESTATISTICS
ora_sysevent
ora_login_user
ora_instance_num
ora_database_name
ora_dict_obj_name
ora_dict_obj_type
ora_dict_obj_owner
ora_dict_obj_name_list
ora_dict_obj_owner_list
BEFOREAUDIT
AFTERAUDIT
Whenanauditornoauditstatementis
issued
ora_sysevent
ora_login_user
ora_instance_num
BEFORENOAUDIT
ora_database_name
AFTERNOAUDIT
BEFORECOMMENT
Whenanobjectiscommented
ora_sysevent
ora_login_user
AFTERCOMMENT
ora_instance_num
ora_database_name
ora_dict_obj_name
ora_dict_obj_type
ora_dict_obj_owner
BEFORECREATE
Whenacatalogobjectiscreated.
ora_sysevent
ora_login_user
AFTERCREATE
ora_instance_num
ora_database_name
ora_dict_obj_type
ora_dict_obj_name
ora_dict_obj_owner
ora_is_creating_nested_table
(forCREATETABLEevents)
WhenmostSQLDDLstatementsareissued.
Notfiredfor ALTERDATABASE,
CREATECONTROLFILE, CREATEDATABASE,
BEFOREDDL
AFTERDDL
andDDLissuedthroughthePL/SQL
procedureinterface,suchascreatingan
advancedqueue.
ora_sysevent
ora_login_user
ora_instance_num
ora_database_name
ora_dict_obj_name
ora_dict_obj_type
ora_dict_obj_owner
BEFOREDISASSOCIATESTATISTICS
Whenadisassociatestatisticsstatementis
issued
AFTERDISASSOCIATESTATISTICS
ora_sysevent
ora_login_user
ora_instance_num
ora_database_name
ora_dict_obj_name
ora_dict_obj_type
ora_dict_obj_owner
ora_dict_obj_name_list
ora_dict_obj_owner_list
BEFOREGRANT
Whenagrantstatementisissued
ora_sysevent
ora_login_user
AFTERGRANT
ora_instance_num
ora_database_name
ora_dict_obj_name
ora_dict_obj_type
ora_dict_obj_owner
ora_grantee
ora_with_grant_option
ora_privileges
BEFORELOGOFF
Atthestartofauserlogoff
ora_sysevent
ora_login_user
ora_instance_num
ora_database_name
AFTERLOGON
Afterasuccessfullogonofauser.
ora_sysevent
ora_login_user
ora_instance_num
ora_database_name
ora_client_ip_address
BEFORERENAME
Whenarenamestatementisissued.
ora_sysevent
ora_login_user
AFTERRENAME
ora_instance_num
ora_database_name
ora_dict_obj_name
ora_dict_obj_owner
ora_dict_obj_type
BEFOREREVOKE
Whenarevokestatementisissued
ora_sysevent
ora_login_user
AFTERREVOKE
ora_instance_num
ora_database_name
ora_dict_obj_name
ora_dict_obj_type
ora_dict_obj_owner
ora_revokee
ora_privileges
AFTERSUSPEND
AfteraSQLstatementissuspended
becauseofanoutofspacecondition.The
triggershouldcorrecttheconditionsothe
statementcanberesumed.
ora_sysevent
ora_login_user
ora_instance_num
ora_database_name
ora_server_error
ora_is_servererror
space_error_info
BEFORETRUNCATE
Whenanobjectistruncated
ora_sysevent
ora_login_user
AFTERTRUNCATE
ora_instance_num
ora_database_name
ora_dict_obj_name
ora_dict_obj_type
ora_dict_obj_owner
ContactUs(http://www.oracle.com/us/corporate/contact/index.html)
YourPrivacyRights(http://www.oracle.com/us/legal/privacy/index.html)
Copyright2014,Oracleand/oritsaffiliates.Allrightsreserved.
LegalNotices(http://www.oracle.com/us/legal/index.html)
TermsofUse
AboutOracle(http://www.oracle.com/corporate/index.html)
(http://www.oracle.com/us/legal/terms/index.html)