Triggers


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:

  1. Execute the BEFORE statement-level trigger, if present.
  2. For each row affected by the statement:
    a. Execute the BEFORE row-level trigger, if present
    b. Execute the DML statement itself.
    c. Execute the AFTER row-level trigger, if present.
  1. Execute the AFTER statement-level trigger, if present.

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]]