TRIGGERS
A database trigger is a named
PL/SQL block stored in a database and exe-cuted
implicitly when a triggering event occurs. An act of executing a
trig-ger
is referred to as firing a trigger. A triggering event is a DML
(INSERT,
UPDATE, or DELETE) statement
executed against a database table. A trig-ger
can fire before or after a
triggering event. For example, if you have de-fined
a trigger to fire before an
INSERT statement on the Customers table,
this trigger fires each time
before you insert a row in the Customers table.
The general syntax for creating
a trigger is as follows (the reserved words
and phrases surrounded by
brackets are optional):
CREATE [OR REPLACE] TRIGGER trigger_name
{BEFORE|AFTER} triggering_event ON
table_name
[FOR EACH ROW]
[WHEN condition]
DECLARE
Declaration statements
BEGIN
Executable statements
EXCEPTION
Exception-handling
statements
END;
The reserved word CREATE specifies
that you are creating a new trigger.
The reserved word REPLACE
specifies that you are modifying an existing
trigger.
REPLACE is optional. However, notice that, most of the time,
both CREATE and REPLACE are
present. Consider the following situation.
You create a trigger as
follows:
CREATE TRIGGER trigger_name
In a few days you decide to
modify this trigger. If you do not include the
reserved word REPLACE in the
CREATE clause of the trigger, this gener-ates
an error message when you
compile the trigger. The error message
states the name of your trigger
is already used by another object. Once
REPLACE is included in the
CREATE clause of the trigger, there is less of a
chance for an error because, if
it is a new trigger, it is created, and if it is
an old trigger, it is replaced.
The trigger_name references the name of the
trigger. BEFORE or AFTER
specify when the trigger fires
(before or after the triggering event). The
triggering_event references a DML statement
issued against the table.
The table_name is the name of the table
associated with the trigger. The
clause, FOR EACH ROW, specifies
a trigger is a row trigger and fires once
for each row either inserted,
updated, or deleted. You will encounter row
and statement triggers in the
next lab of this chapter. A WHEN clause
specifies a condition that must evaluate to TRUE for
the trigger to fire. For
example, this condition may
specify a certain restriction on the column
of a table. Next, the trigger
body is defined. It is important for you to real-ize
if you drop a table, the
tables database triggers are dropped as well.
However, you should be careful
when using the reserved word REPLACE for
a number of reasons. First, if
you happen to use REPLACE and a name of an
existing stored function, procedure,
or package, it will be replaced by the
trigger. Second, when you use
the reserved word REPLACE and decide to as-sociate
a different table with your
trigger an error message is generated. For
example, assume you created a
trigger Customer_BI on the Customers table.
Next, you decided to modify
this trigger and associate it with the Bookings
table. As a result, the
following error message is generated:
ERROR at line 1:
ORA-04095: trigger
'Customers_BI' already exists on an-other
table, cannot replace it
Triggers are used for different
purposes. Some uses for triggers are:
Enforcing complex business
rules that cannot be defined by
using integrity constraints.
Maintaining complex security
rules.
Automatically generating
values for derived columns.
Collecting statistical
information on table accesses.
Preventing invalid
transactions.
Providing value auditing.
The body of a trigger is a
PL/SQL block. However, there are several restrictions
that you need to know to create
a trigger. These restrictions are:
A trigger may not issue a transactional control statement such
as COMMIT, SAVEPOINT, or
ROLLBACK. When the trigger
fires, all operations performed
become part of a transaction.
When this transaction is
committed or rolled back, the operations
performed by the trigger are
committed or rolled back as
well.
Any function or a procedure
called by a trigger may not issue a
transactional control
statement.
It is not permissible to
declare LONG or LONG RAW variables
in the body of a trigger.
Example:
CREATE OR REPLACE TRIGGER HW5
BEFORE INSERT ON bookings
FOR EACH ROW
----------
Declarations ----------
DECLARE
v_severity Number(1);
early_date bookings.date_rent_start%TYPE;
max_debt bookings.amount_due%TYPE;
severity_1_exc EXCEPTION;
severity_2_exc EXCEPTION;
severity_3_exc EXCEPTION;
no_data_exc EXCEPTION;
BEGIN
DBMS_OUTPUT.enable;
-- v_severity := 1;
SELECT MIN(date_rent_start),
MAX(amount_due)
INTO early_date,
max_debt
FROM bookings
WHERE cust_no = :new.cust_no
AND paid
<> 'Y'
AND SYSDATE
- date_rent_start >= 90
AND amount_due
>= 200;
IF max_debt IS NULL THEN
RAISE no_data_exc;
END IF;
IF SYSDATE - early_date >=
365 THEN
RAISE severity_3_exc;
ELSE IF SYSDATE - early_date
>= 180
OR max_debt >= 500 THEN
RAISE severity_2_exc;
ELSE
RAISE severity_1_exc;
END IF;
END IF;
EXCEPTION
WHEN severity_1_exc THEN
DBMS_OUTPUT.PUT_LINE('Severity 1 - This person must PAY!');
WHEN severity_2_exc THEN
DBMS_OUTPUT.PUT_LINE('Severity 2 - This person must PAY!');
WHEN severity_3_exc THEN
DBMS_OUTPUT.PUT_LINE('Severity 3 - This person must PAY!');
WHEN no_data_exc THEN
DBMS_OUTPUT.PUT_LINE('no_data_exc');
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('No credit history found on his guy.');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Sorry. An error has occurred.');
END;
/
show errors
More Examples
drop table repair_header_log;
create table Repair_Header_Log
(Repair_ID number(4),
Customer_ID number(4),
Date_Deleted date,
User_Deleting
varchar2(30));
create or replace trigger Repair_Header_Delete_After after
delete on Repair_Header
for each row
declare
i number;
begin
insert into Repair_Header_Log
(Repair_ID, Customer_ID,
Date_Deleted, User_Deleting)
values
(:old.Repair_ID,
:old.Customer_ID, SYSDATE, USER);
end;
drop table Credit_Charge_Log;
drop table Credit_Charge_Attempt_Log;
drop table Credit_Master;
create table Credit_Charge_Log
(Card_Number varchar2(16),
Amount
number(9,2),
Vendor_ID
varchar2(8),
Transaction_Date date);
create table Credit_Charge_Attempt_Log
(Card_Number varchar2(16),
Amount
number(9,2),
Vendor_ID
varchar2(8),
Transaction_Date date);
create table Credit_Master
(Card_Number varchar2(16),
Last_Name
varchar2(30),
First_Name
varchar2(20));
create or replace trigger Credit_Charge_Log_Ins_Before before
insert on Credit_Charge_Log
for each row
declare
total_for_past_3_days number;
begin
-- Check the credit charges for the past 3
days.
-- If they total more than $1000.00, log this
entry
-- in the Credit_Charge_Attempt_Log for further
handling.
select sum(amount)
into total_for_past_3_days
from Credit_Charge_Log
where
Card_Number =
:new.Card_Number and
Transaction_Date >=
sysdate-3;
if total_for_past_3_days >
1000.00 then
insert into Credit_Charge_Attempt_Log
(Card_Number, Amount, Vendor_ID, Transaction_Date)
values
(:new.Card_Number, :new.Amount, :new.Vendor_ID,
:new.Transaction_Date);
end if;
end;
/
---------------------------------------------------------------------------------------------------
drop table Block_Trade_Log;
create table Block_Trade_Log
(Stock_Symbol varchar2(6),
Price number,
Blocks_Traded number,
Bought_Sold char(1),
Running_Avg_3_Days number,
Timestamp date);
create or replace trigger Block_Trade_Log_Ins_Before before
insert on Block_Trade_Log
for each row
declare
Running_Avg number := 0;
begin
select avg(price)
into Running_Avg
from Block_Trade_Log
where
Stock_Symbol =
:new.Stock_Symbol and
Timestamp >= SYSDATE-3;
:new.Running_Avg_3_Days :=
Running_Avg;
end;
/
create or replace trigger Block_Trade_Log_Ins_After after
insert on Block_Trade_Log
for each row
declare
Too_Many_Blocks exception;
begin
if :new.Blocks_Traded >
1000000 then
raise Too_Many_Blocks;
end if;
exception
when Too_Many_Blocks then
raise_application_error (-20001, 'You cannot trade more than
one million blocks');
end;
-----------------------------------------------------------------------------------------------
drop table Shipment;
create table Shipment
(Shipment_ID varchar2(12),
Product_Code
varchar2(12),
Quantity number,
Customer_ID
varchar2(12),
Manual_Check char(1) check (Manual_Check
in ('Y','N')),
Entered_By
varchar2(30),
Entered_On date);
create or replace trigger Shipment_Ins_Before before
insert on Shipment
for each row
when (to_number(to_char(sysdate,'HH24'))
> 17)
declare
begin
:new.Manual_Check := 'Y';
end;
/
rem Example of Cascading
Triggers
drop table tab1;
drop table tab2;
drop table tab3;
create table tab1
(col1 number);
create table tab2
(col2 number);
create table tab3
(col3 number);
create or replace trigger tab1_Update_Before before
update on tab1
for each row
declare
begin
insert into tab2
(col2)
values
(:old.col1);
end;
/
create or replace trigger tab2_Insert_Before before
insert on tab2
for each row
declare
begin
update tab3
set
col3 = :new.col2;
end;
/
create or replace trigger tab3_Update_After after
update on tab3
declare
begin
insert into tab3
(col3)
values
(27);
end;
/