What are triggers?
A trigger defines an action the database should take when some database-related event (such as inserts, updates, deletes) occurs.
When triggers are used?
CREATE [OR REPLACE] TRIGGER
trigger_name
{BEFORE
| AFTER} {INSERT|UPDATE|DELETE} [OF
column]
ON
table_reference
[FOR
EACH ROW [WHEN trigger_condition]]
[DECLARE] -- optional, for declaring local variables
trigger_body;
Note that the DECLARE keyword is back in Trigger!
The Trigger_body is
executed when an event (Insert, Update, Delete operation) occurs.
Trigger names
Triggers exist in a separate namespace from procedure, package, tables (that share the same namespace), which means that a trigger can have the same name as a table or procedure.
Types of triggers: row-level triggers and statement-level triggers
Row-level triggers for data-related activities
Row-level triggers execute once for each row in a
transaction.
Row-level triggers are the most common type of triggers; they
are often used in data auditing applications.
Row-level trigger is
identified by the FOR EACH ROW clause in the CREATE TRIGGER
command.
Statement-level triggers for transaction-related activities
Statement-level triggers execute once for each
transaction. For example, if a single transaction inserted
500 rows into the Customer table, then a statement-level
trigger on that table would only be executed once.
Statement-level triggers
therefore are not often used for data-related activities; they are
normally used to enforce additional security measures on the
types of transactions that may be performed on a table.
Statement-level
triggers are the default type of triggers created and are identified by
omitting the FOR EACH ROW clause in the CREATE TRIGGER command.
Before and After Triggers
Clearly, there is a great deal of flexibility in the design of a trigger.
Valid trigger types (possible combination of triggers)
Statement (INSERT, DELETE, UPDATE),
Timing (BEFORE,
AFTER), Level (Row-level,
Statement-level)
The values for the statement, timing, and
level determine the types of the triggers. There are total of 12 possible types
of triggers: 3*2*2 = 12
Combining Trigger Types
EXAMPLE 1: -- for the table major_stats (major, total_credits, total_students);
CREATE OR REPLACE TRIGGER
updateMajorStats
AFTER
INSERT OR DELETE OR UPDATE ON
students -- Oracle will check the status of this table
DECLARE -- unlike a procedure, use DECLARE
keyword
CURSOR c_statistics
IS
SELECT
major, COUNT(*) total_students, SUM(current_credits)
total_credits
FROM
students
GROUP
BY major;
BEGIN
FOR v_statsRecord
IN c_statistics
LOOP
UPDATE
major_stats
SET
total_credits =
v_statsRecord.total_credits,
total_students
=
v_statsRecord.total_students
WHERE
major = v_statsRecord.major;
IF SQL%NOTFOUND
THEN
INSERT
INTO major_stats(major, total_credits,
total_students)
VALUES(v_statsRecord.major,
v_statsRecord.total_credits,
v_statsRecord.total_students);
END
IF;
END LOOP;
END
updateMajorStats;
Order of Trigger Firing
Triggers are fired as the DML statement is executed. The algorithm for executing a DML statement is given here:
To illustrate this, suppose we have all four kinds of UPDATE triggers defined on the table, classes before and after at statement level and row level. Suppose we then issue the following UPDATE statement that affects four rows:
UPDATE classes
SET num_credits = 4
WHERE department IN ('HIS', 'CS');
Using pseudo variable :old and :new in Row-level Triggers only
EXAMPLE 2: For example, the GenerateStudentID trigger shown next uses :new. Its purpose is to fill in the ID field of Students with a value generated from the student_sequence sequence.
CREATE OR REPLACE TRIGGER
GenerateStudentID
BEFORE
INSERT OR UPDATE ON
students
FOR
EACH ROW
BEGIN
SELECT
student_sequence.nextval INTO
:new.ID
FROM
dual;
END GenerateStudentID;
The trigger GenerateStudentID actually modifies the value of :new.ID. This is one of the useful features of :new -- when the statement is actually executed, whatever values are in :new will be used.
INSERT INTO students(first_name, last_name) VALUES('Lolita', 'Lazarus');
We can issue an INSERT statement without generating an error. Even though we haven't specified a value for the primary column ID (which is required), the trigger will supply it.
=> If we specify a value for ID, it will be ignored, since the trigger changes it.
The WHEN clause -- WHEN condition
EXAMPLE 3: For example, suppose we want to monitor any adjustments to an Amount that are greater than 10 percent.
CREATE TRIGGER
customer_bef_upd_row
BEFORE
UPDATE ON
customer
FOR
EACH ROW
WHEN
(new.amount/old.amount > 1.1)
-- note
that there is no colon : for new and
old
--
missing the colon is not an
error.
BEGIN
INSERT
INTO customer_audit
--
different table from the triggering
table
VALUES
(:old.Action_date, :old.Action, :old.Item, :old.qty, :old.qtyType, :old.rate,
:old.amount)
--
the colon : is back in the trigger body.
END;
Using Trigger Predicates: INSERTING, UPDATING, and DELETING
EXAMPLE 4: We can use three boolean functions to determine what the operation is (since you can have several triggering operations, sometimes you need to determine which operation is performing now).
CREATE TRIGGER
customer_bef_upd_ins_row
BEFORE
INSERT OR UPDATE OF Amount ON
CUSTOMER
FOR
EACH
ROW
BEGIN
IF
INSERTING
THEN
INSERT
INTO
customer_audit
VALUES(:new.Action_date,
:new.Action, :new.Item, :new.qty,
:new.qtyType,
:new.rate,
:new.Amount);
ELSE
--
if not inserting, then we are updating
Amount
INSERT
INTO
customer_audit
VALUES(:old.Action_date,
:old.Action, :old.Item, :old.qty,
:old.qtyType,
:old.rate,
:old.Amount);
END
IF;
END;
Dictionary table for triggers
USER_TRIGGERS
Triggers and Dependencies
A trigger has a separate name space from a procedure/function or package, which means there is no naming conflict with a procedure. Since the trigger is stored in source format, it does not depend on anything.
Restrictions on Triggers
A trigger may not issue any transaction control statement (e.g., COMMIT,
ROLLBACK,
)
The trigger body cannot declare any LONG variables.
Row-level triggers may not access mutating or constraining tables.
A mutating table is a table that is currently being modified by a
DML statement (e.g., a table on which the trigger is defined).
A constraining table is a table that is related to a mutating
table through foreign key (it's a table that might need to be read from)
Trigger related commands
DROP TRIGGER trigger-name
ALTER TRIGGER trigger-name [{DISABLE |
ENABLE }] -- unlike procedures,
packages
ALTER TABLE table_name [{DISABLE | ENABLE } [ALL TRIGGERS]]