Upload
midhungbabu88
View
217
Download
0
Embed Size (px)
Citation preview
8/9/2019 Trigger Writing For Fun and Profit
1/32
A Community of Learning
SUNGARD SUMMIT 2007 | sungardsummit.com 1
Trigger Writing
for Fun and Profit
Presented by: Larry Holder
Database Administrator
The University of Tennessee at Martin
March 21, 2007
Course ID: 2
8/9/2019 Trigger Writing For Fun and Profit
2/32
2Course ID 2
Session Rules of Etiquette
Please set your cell phone or pager on stun.
If you must leave the session early, please do so as
discreetly as possible, and dont let the door hit you on
the way out.
Please avoid side conversation during the session,
unless you are discussing what a fantastic job Im doing
up here...
Thanks a Gigabyte!
8/9/2019 Trigger Writing For Fun and Profit
3/32
3Course ID 2
Introduction
This session provides an introduction to writing
database triggers, along with several real-world
examples that can enhance your support and
functionality of Banner.
8/9/2019 Trigger Writing For Fun and Profit
4/32
A Community of Learning
SUNGARD SUMMIT 2007 | sungardsummit.com 4
Whats a Trigger?
8/9/2019 Trigger Writing For Fun and Profit
5/32
5Course ID 2
Whats a Trigger?
We aint talkin
about Roys
noble steed...
8/9/2019 Trigger Writing For Fun and Profit
6/32
6Course ID 2
Ok, so... whats a trigger?
A PL/SQL object, akin to a Procedure.
Most often, a cool, calm & calculated reaction to an
event, such as insert, update, delete, or even select, ona table.
Sometimes, a reaction to a database event, such as a
user logging onto the database.
8/9/2019 Trigger Writing For Fun and Profit
7/32
7Course ID 2
What can I do with it?
Modify a field before it is inserted or updated.
Insert, update, or delete data in other tables.
Perform additional error-checking, and prevent an
unwanted insert, update, or delete from occurring.
Notify someone by email about an event.
8/9/2019 Trigger Writing For Fun and Profit
8/32
8Course ID 2
What schema owns the custom triggers?
We chose to let SATURN own our custom triggers
associated with SATURN-owned tables; likewise for
GENERAL, WTAILOR, etc.
Be sure the owner of the trigger is granted rights on anytables it references or modifies that are owned by
another schema.
8/9/2019 Trigger Writing For Fun and Profit
9/32
9Course ID 2
What naming convention is used?
UTM_ST_SARADAP_PRE_IU_ROW
A pre-insert / update trigger, at the row level, on the
table SARADAP. UTM is our prefix, and ST is
standard for Student Trigger.
Remember30-character object name max within
Oracle.
Add numeric suffix if a tie-breaker is needed (multiple
triggers on same table are allowed).
8/9/2019 Trigger Writing For Fun and Profit
10/32
10Course ID 2
Special variables
:old.spraddr_street_line1
meaningless for INSERT, will yield NULL
:new.spraddr_street_line1
8/9/2019 Trigger Writing For Fun and Profit
11/32
11Course ID 2
Special variables
INSERTING, UPDATING, DELETING
Useful if trigger handles more than one mode, such
as both INSERT and UPDATE, and your logic needs
to know which occurred.
IF INSERTING THENdo this;
ELSEdo that;
END IF;
8/9/2019 Trigger Writing For Fun and Profit
12/32
12Course ID 2
Special variables
USER - the Oracle userid of who is connected
For self-service, keep in mind that the userid is likely
something like WWW_USER.
SYSDATE - the current date and time
DATABASE_NAME
Useful to differentiate between production and test:
IF substr(DATABASE_NAME,1,4) = 'PROD'
8/9/2019 Trigger Writing For Fun and Profit
13/32
13Course ID 2
Examples of Before/After & Row/Statement
create or replace trigger trigger_name
BEFORE INSERT or UPDATE on table_name for each ROW
create or replace trigger trigger_name
AFTERDELETE on table_name
statement level if no
"for each row"Firing order:
1. Before Statement (once only)
2. Before Row (once per each affected row)3. The actual DML statement
4. After Row (once per each affected row)
5. AfterStatement (once only)
8/9/2019 Trigger Writing For Fun and Profit
14/32
14Course ID 2
Example: Modifying fields "before"
create or replace trigger utm_st_sarpers_pre_in_up_row
BEFORE INSERT or UPDATE on SARPERS for each ROW
BEGIN
IF :new.sarpers_last_name IS NOT NULL THEN
IF :new.sarpers_last_name = UPPER(:new.sarpers_last_name) OR
:new.sarpers_last_name = LOWER(:new.sarpers_last_name) THEN
:new.sarpers_prefix := INITCAP(:new.sarpers_prefix);
:new.sarpers_first_name := INITCAP(:new.sarpers_first_name);
:new.sarpers_middle_name1 := INITCAP(:new.sarpers_middle_name1);
:new.sarpers_middle_name2 := INITCAP(:new.sarpers_middle_name2);
:new.sarpers_last_name := INITCAP(:new.sarpers_last_name);
:new.sarpers_suffix := INITCAP(:new.sarpers_suffix);
:new.sarpers_nickname := INITCAP(:new.sarpers_nickname);
:new.sarpers_combined_name := INITCAP(:new.sarpers_combined_name);
:new.sarpers_former_name := INITCAP(:new.sarpers_former_name);
END IF;
END IF;
END;
/
8/9/2019 Trigger Writing For Fun and Profit
15/32
15Course ID 2
Example: Writing to an additional table
create or replace trigger utm_st_sarctrl_post_ins_row
AFTER INSERT on SARCTRL for each ROW
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
insert into saraatt
values (:new.sarctrl_pidm,
:new.sarctrl_term_code_entry,
:new.sarctrl_appl_no_saradap,
'WAPP',
sysdate);
COMMIT;
EXCEPTION
WHEN OTHERS THEN
NULL;
COMMIT;
END;
/
Note the use, in this
example, of
autonomoustransaction, if you wish
or need to commit an
action independently of
the transaction causing
the trigger. Otherwise, a
trigger cannot include acommit.
8/9/2019 Trigger Writing For Fun and Profit
16/32
16Course ID 2
Example: Additional error checking
c
reate or replac
e trigger utm_st_spriden_pre_insert_rowBEFORE INSERT on SPRIDEN for each ROWBEGIN
IF :new.spriden_change_ind IS NULL THENIF (SUBSTR(:new.spriden_id,1,3) = '960' OR
SUBSTR(:new.spriden_id,1,1) = '-')OR(SUBSTR(:new.spriden_id,1,1) > '9' AND :new.spriden_entity_ind = 'C')
OR(LENGTH(:new.spriden_id)
8/9/2019 Trigger Writing For Fun and Profit
17/32
17Course ID 2
Example: Sending an email notification
create or replace trigger utm_st_sortest_post_del_row
after DELETE on SORTEST for each ROW
w_conn UTL_SMTP.connection;w_crlf varchar2(2) := CHR(13) || CHR(10); w_mailhost varchar2(30) := 'your_email_server'; w_mesg varchar2(4000); w_from varchar2(30) := 'your_from_address'; w_to varchar2(30) := 'your_recipient_address';
w_test_email varchar2(30) := 'your_reciepient_for_non_production'; w_stu_id varchar2(9); w_stu_name varchar2(100);
BEGINBEGIN
IF SUBSTR(DATABASE_NAME,1,4) != 'PROD' THENw_to := w_test_email;
END IF;
select spriden_id, spriden_first_name || ' ' || spriden_last_nameinto w_stu_id, w_stu_namefrom spriden
where spriden_pidm = :old.sortest_pidmand spriden_change_ind is null; continued . . .
8/9/2019 Trigger Writing For Fun and Profit
18/32
18Course ID 2
Example: Sending an email... (continued)
w_mesg :='Date: ' || TO_CHAR(SYSDATE,'DD Mon YY HH24:MI:SS') || w_crlf ||'From: ' || w_from || w_crlf ||'Subject: ' || 'Deletion from SORTEST (SOATEST)' || w_crlf ||'To: ' || w_to || w_crlf || w_crlf ||user || ' deleted an entry for ' || w_stu_name || ' (' || w_stu_id || ') of' ||': code = ' || :old.sortest_tesc_code ||', date = ' || to_char(:old.sortest_test_date,'MM-DD-YYYY') ||', score = ' || :old.sortest_test_score ||
w_c
rlf || w_c
rlf || ' ' || w_c
rlf;
w_conn := UTL_SMTP.open_connection(w_mailhost, 25);UTL_SMTP.helo(w_conn, w_mailhost);UTL_SMTP.mail(w_conn, w_from);UTL_SMTP.rcpt(w_conn, w_to);UTL_SMTP.data(w_conn, w_mesg);UTL_SMTP.quit(w_conn);
EXCEPTIONWHEN OTHERS THEN NULL;
END;END;/
8/9/2019 Trigger Writing For Fun and Profit
19/32
19Course ID 2
Disabling and enabling a trigger
alter trigger [owner.]name DISABLE;
alter trigger [owner.]name ENABLE;
Automatically enabled when initially created.
select * from dba_triggerswhere trigger_name like 'UTM%';
8/9/2019 Trigger Writing For Fun and Profit
20/32
20Course ID 2
Mutating trigger example
BEGIN
w_pidm := :new.spbpers_pidm; w_new_ssn := :new.spbpers_ssn;
IF :new.spbpers_ssn IS NOT NULL THENOPEN check_for_dup;FETCH check_for_dup INTO w_flag;CLOSE check_for_dup;
IF w_flag = 'Y' THENRAISE_APPLICATION_ERROR(-20501,'*** SSN already in use ***');
END IF;END IF;
END utm_junk1;/
create or replace trigger utm_junk1
BEFORE insert or update
of spbpers_ssn on SPBPERS
for each ROW
DECLARE
w_pidm spbpers.spbpers_pidm%type;
w_new_ssn spbpers.spbpers_ssn%type;w_flag char(1) := NULL;
CURSORcheck_for_dup IS
select distinct 'Y'
fromspbpers
where spbpers_ssn = w_new_ssn
and spbpers_pidm != w_pidm;
8/9/2019 Trigger Writing For Fun and Profit
21/32
21Course ID 2
Results (mutation) ...
ORA-04091: table SATURN.SPBPERS is mutating,trigger/function may not see it
ORA-06512: at "SATURN.UTM_JUNK1", line xx
ORA-06512: at "SATURN.UTM_JUNK1", line xxORA-04088: error during execution of trigger
'SATURN.UTM_JUNK1'
8/9/2019 Trigger Writing For Fun and Profit
22/32
22Course ID 2
Solution, part 1 of3 (package)
create or replace package utm_st_spbpers_pkg as
TYPE t_pidms IS TABLE OF spbpers.spbpers_pidm%TYPE INDEX BY BINARY_INTEGER;
TYPE t_new_ssns IS TABLE OF spbpers.spbpers_ssn%TYPE INDEX BY BINARY_INTEGER;
pidms t_pidms;
new_ssns t_new_ssns;
num_entries BINARY_INTEGER := 0;
END utm_st_spbpers_pkg;
/
8/9/2019 Trigger Writing For Fun and Profit
23/32
23Course ID 2
Solution, part 2 of3 (before / row)
create or replace trigger utm_st_spbpers_pre_iu_row
BEFORE insert or update of spbpers_ssn on SPBPERS
for each ROW
BEGIN
utm_st_spbpers_pkg.num_entries := utm_st_spbpers_pkg.num_entries + 1;
utm_st_spbpers_pkg.pidms (utm_st_spbpers_pkg.num_entries) :=
:new.spbpers_pidm;
utm_st_spbpers_pkg.new_ssns (utm_st_spbpers_pkg.num_entries) :=
:new.spbpers_ssn;
END utm_st_spbpers_pre_iu_row;
8/9/2019 Trigger Writing For Fun and Profit
24/32
24Course ID 2
Solution, part 3 of3 (after / statement)
create or replace trigger utm_st_spbpers_post_iu_stmt
AFTER insert or update of spbpers_ssn on SPBPERS
DECLARE
w_num_entries utm_st_spbpers_pkg.num_entries%type;
w_pidm spbpers.spbpers_pidm%type; w_new_ssn spbpers.spbpers_ssn%type;
w_flag char(1) := NULL;
w_error char(1) := 'N';
CURSORcheck_for_dup IS
select distinct 'Y'
from spbpers
where spbpers_ssn = w_new_ssn
and spbpers_pidm != w_pidm;
continued...
8/9/2019 Trigger Writing For Fun and Profit
25/32
25Course ID 2
BEGIN
w_num_entries := utm_st_spbpers_pkg.num_entries;
FOR z_index IN 1..w_num_entries LOOP
w_pidm := utm_st_spbpers_pkg.pidms (z_index);
w_new_ssn := utm_st_spbpers_pkg.new_ssns (z_index);
IF w_new_ssn IS NOT NULL THENOPEN check_for_dup;
FETCH check_for_dup INTO w_flag;
CLOSE check_for_dup;
IF w_flag = 'Y' THEN
w_error := 'Y';
END IF;
END IF;
END LOOP;
continued...
8/9/2019 Trigger Writing For Fun and Profit
26/32
26Course ID 2
IF w_error = 'Y' THEN
utm_st_spbpers_pkg.num_entries := 0;
RAISE_APPLICATION_ERROR(-20501, '*** SSN ALREADY IN USE ***');END IF;
utm_st_spbpers_pkg.num_entries := 0;
END utm_st_spbpers_post_iu_stmt;
/
8/9/2019 Trigger Writing For Fun and Profit
27/32
27Course ID 2
New Results (no mutation) ...
ORA-20501: *** SSN ALREADY IN USE ***
ORA-06512: at "SATURN.UTM_SPBPERS_POST_IU_STMT", line xx
ORA-04088: error during execution of trigger
'SATURN.UTM_SPBPERS_POST_IU_STMT'
This is expected
This is the application error
that we intentionally raised
8/9/2019 Trigger Writing For Fun and Profit
28/32
28Course ID 2
Banner7 "BTW..."
I've found that the new API logic of Banner7 has changed a few things...
for example, the column SGBSTDN_LEVL_CODE is still NULL wheninitially inserted; a subsequent Update populates it. I changed
several POST-INSERT triggers to POST-INSERT/UPDATE and added
the following logic at the beginning, to handle the initialsetting of the
column regardless of whether done by Insert or Update...
IF (INSERTING AND :new.sgbstdn_levl_code IS NULL) OR
(UPDATING AND :old.sgbstdn_levl_code IS NOT NULL) THEN
GOTO skip_everything;
END IF;
...
NULL;
8/9/2019 Trigger Writing For Fun and Profit
29/32
29Course ID 2
Helpful Debugging Tip
Consider creating a table to hold debug info, which you can write to
from any trigger that you are testing... to show you values in :oldand :new, for example, plus anything else you care to review. I used
this to debug the Post-Insert triggers mentioned on the previous
slide...
Create Table Trigger_Debug
(trigger_name varchar2(30) not null,
datetime date not null,codepoint varchar2(30) not null,
info1 varchar2(100),
info2 varchar2(100),
info3 varchar2(100);
Create Public Synonym Trigger_Debug for Trigger_Debug;
Grant All on Trigger_Debug to Public;
w_debug char(1) := 'Y'; turn this on oroff as needed
IF w_debug = 'Y' THEN
INSERT INTO TRIGGER_DEBUG VALUES
('MY_TRIGGER',SYSDATE,'BEGIN',
:OLD.SGBSTDN_LEVL_CODE, :NEW.SGBSTDN_LEVL_CODE, NULL);
8/9/2019 Trigger Writing For Fun and Profit
30/32
30Course ID 2
Summary
Triggers are a great way to extend the functionality of
Banner without introducing baseline modifications.
Rome wasnt built in a day. Start out simply, and add
more at your own pace.
8/9/2019 Trigger Writing For Fun and Profit
31/32
31Course ID 2
Questions & Answers
Ok, its your turn...
8/9/2019 Trigger Writing For Fun and Profit
32/32
32Course ID 2
Thank You!
Larry Holder
www.utm.edu/staff/lholder/dba
Please complete the online
class evaluation form
Course ID: 2
SunGard, the SunGard logo, Banner, Campus Pipeline, Luminis, PowerCAMPUS, Matrix, and Plus are
trademarks or registered trademarks ofSunGard Data Systems Inc. or its subsidiaries in the U.S. and other
countries. Third-party names and marks referenced herein are trademarks or registered trademarks of their
respective owners.
General presentation Copyright 2006 Larry Holder (of The University of Tennessee at Martin).
Portions 2006SunGard. All rights reserved.
Photo of Roy Rogers and Trigger used by permission of the Roy Rogers-Dale Evans Museum, Branson, MO.
No animals were harmed in the production of this presentation. No artificial ingredients added.