What are triggers?
A trigger defines an action the database should take when some database-related event (such as inserts, updates, deletes) occurs.
• Triggers are similar to procedures, in that they are named PL/SQL blocks.
Differences between Procedures and Triggers
A procedure is executed explicitly from another block via a procedure call with passing arguments, while a trigger is executed (or fired) implicitly whenever the triggering event (DML: INSERT, UPDATE, or DELETE) happens, and a trigger doesn't accept arguments.
When triggers are used?
 • Maintaining complex integrity constraints (referential integrity) 
  or business rules
  • Auditing information in a table by recording the changes.
  • Automatically signaling other programs that action needs to take place 
  when changes are made to a table
  • Collecting/maintaining statistical data.
 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
There are two types of triggers in Oracle including 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
 • Since triggers occur because of events, they may be set to occur immediately 
  before or after those events. The events that    execute triggers 
  are database transactions, triggers can be executed immediately BEFORE or AFTER 
  the statements    INSERTs, UPDATEs, DELETEs.
  • AFTER row-level triggers are frequently used in auditing applications, 
  since they do not fire until the row has been modified.
    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
 • Triggers for multiple INSERT, UPDATE, DELETE commands on a table can 
  be combined into a single trigger (using OR),    provided 
  they are all at the same level (row-level or statement-level), e.g., INSERT 
  OR UPDATE OR DELETE.
  • However, you can not combine BEFORE or AFTER, e.g., BEFORE OR AFTER 
  is illegal.
An Example of Trigger -- 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;
Using pseudo variable :old and :new in Row-level Triggers only
 • A row-level trigger fires once per row processed by the triggering 
  statement. Inside the trigger, you can access the row that is    currently 
  being processed through two pseudocodes -- :old and 
  :new, e.g., :new.field or :old.field. 
  However, they are not    true records.
  • :old refers to the data as it existed prior to the 
  transaction. Updates and Deletes usually reference :old values. 
  The :new values    are the data values that the transaction creates 
  (such as the columns in an inserted record).
  • :old is undefined for INSERT statements, :new 
  is undefined for DELETE statements.
  • If you need to set a column value in an inserted row via your trigger, 
  then you will need to use a BEFORE INSERT trigger in   order to access 
  the :new values. Using an AFTER INSERT trigger would not allow 
  you to set the inserted value, since the row   will already have been 
  inserted into the table (and thus be :old).
  • The colon in front of :new and :old 
  is required. 
  • Operations that would normally be valid on 
  records are not valid for :new and 
  :old. For example, they cannot be assigned    as 
  entire records, e.g., v_tmprec := :old; is not legal. Only 
  the individual fields within them may be assigned, e.g.,     v_tmprec.col 
  := :old.col;.
  • Likewise, :old and :new cannot be 
  passed to procedures or functions that take arguments of triggering_table%ROWTYPE, 
    since the actual parameter is assigned to the formal parameter in 
  the same way as a standard assignment.
For example, the GenerateStudentID trigger shown next uses :new. Its purpose is to fill in the ID field of Students with a vlaue 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
• The WHEN clause is valid for row-level triggers only. If present, the trigger body will be executed only for those rows that meet the condition specified by the WHEN clause.
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;
 • The above row-level BEFORE UPDATE trigger will be executed only if 
  the new value of the Amount column is more than 10    percent 
  greater than its old value.
  • The When clause adds further criteria to the triggering condition. The 
  triggering event must not only be an UPDATE of the    customer 
  table, but also must reflect an increase of over 10 percent in the value of 
  the Amount column.
  • The statements in the BEGIN and END is the trigger body. The commands 
  shown in the BEGIN and END are to be executed    for every UPDATE 
  of the customer table that passes the WHEN condition.
Using Trigger Predicates: INSERTING, UPDATING, and DELETING
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;
 • If you look at the trigger body in the above PL/SQL program, the trigger 
  checks to see if the record is being inserted into the   customer 
  table. If it is, then the first part of the trigger body is executed. The INSERTING 
  portion of the trigger body inserts   the new values of the record 
  into the customer_audit table.
  • Other transaction types can be checked. In this example, since the trigger 
  executed, the transaction must either an INSERT or    an UPDATE 
  of the Amount column.
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. Mutating 
  table is a table that the current trigger is watching    for 
  a modification. Constraining table is a table that is related to a mutating 
  table through foreign key.
Trigger related commands
 • DROP TRIGGER trigger-name
  • ALTER TRIGGER trigger-name [{DISABLE | ENABLE }]
  • ALTER TABLE table_name [{DISABLE | ENABLE } [ALL TRIGGERS]]