Вы находитесь на странице: 1из 26

Oracle Triggers

PL/SQL - Triggers
Triggers are stored programs, which are automatically executed or fired
when some events occur. Triggers are, in fact, written to be executed in
response to any of the following events:
A database manipulation (DML) statement (DELETE, INSERT, or
UPDATE).
A database definition (DDL) statement (CREATE, ALTER, or DROP).
A database operation (SERVERERROR, LOGON, LOGOFF, STARTUP, or
SHUTDOWN).
Triggers could be defined on the table, view, schema, or database with
which the event is associated.
Benefits of Triggers
Triggers can be written for the following purposes:
Generating some derived column values automatically
Enforcing referential integrity
Event logging and storing information on table access
Auditing
Synchronous replication of tables
Imposing security authorizations
Preventing invalid transactions
Creating Triggers
CREATE [OR REPLACE ] TRIGGER trigger_name
{BEFORE | AFTER | INSTEAD OF }
{INSERT [OR] | UPDATE [OR] | DELETE}
[OF col_name]
ON table_name
[REFERENCING OLD AS o NEW AS n]
[FOR EACH ROW]
WHEN (condition)
DECLARE
Declaration-statements
BEGIN
Executable-statements
EXCEPTION
Exception-handling-statements
END;
Creating Triggers
Where,
CREATE [OR REPLACE] TRIGGER trigger_name: Creates or replaces an existing trigger with the trigger_name.
{BEFORE | AFTER | INSTEAD OF} : This specifies when the trigger would be executed. The INSTEAD OF clause is
used for creating trigger on a view.
{INSERT [OR] | UPDATE [OR] | DELETE}: This specifies the DML operation.
[OF col_name]: This specifies the column name that would be updated.
[ON table_name]: This specifies the name of the table associated with the trigger.
[REFERENCING OLD AS o NEW AS n]: This allows you to refer new and old values for various DML statements, like
INSERT, UPDATE, and DELETE.
[FOR EACH ROW]: This specifies a row level trigger, i.e., the trigger would be executed for each row being affected.
Otherwise the trigger will execute just once when the SQL statement is executed, which is called a table level
trigger.
WHEN (condition): This provides a condition for rows for which the trigger would fire. This clause is valid only for
row level triggers.
Example
The following program creates a row level trigger for the customers table that would fire for INSERT or
UPDATE or DELETE operations performed on the CUSTOMERS table. This trigger will display the salary
difference between the old values and new values:
CREATE OR REPLACE TRIGGER display_salary_changes
BEFORE DELETE OR INSERT OR UPDATE ON customers
FOR EACH ROW
WHEN (NEW.ID > 0)
DECLARE
sal_diff number;
BEGIN
sal_diff := :NEW.salary - :OLD.salary;
dbms_output.put_line('Old salary: ' || :OLD.salary);
dbms_output.put_line('New salary: ' || :NEW.salary);
dbms_output.put_line('Salary difference: ' || sal_diff);
END;
/
CREATE OR REPLACE TRIGGER Salary_check
BEFORE INSERT OR UPDATE OF Sal, Job ON Emp99
FOR EACH ROW
DECLARE
Minsal NUMBER;
Maxsal NUMBER;
Salary_out_of_range EXCEPTION;
BEGIN
SELECT Minsal, Maxsal INTO Minsal, Maxsal FROM Salgrade
WHERE Job_classification = :new.Job;
IF (:new.Sal < Minsal OR :new.Sal > Maxsal) THEN
RAISE Salary_out_of_range;
END IF;
EXCEPTION
WHEN Salary_out_of_range THEN
Raise_application_error (-20300,
'Salary '||TO_CHAR(:new.Sal)||' out of range for '
||'job classification '||:new.Job ||' for employee '||:new.Ename);
WHEN NO_DATA_FOUND THEN
Raise_application_error(-20322,
'Invalid Job Classification ||:new.Job_classification);
END;
Considerations
Here following two points are important and should be noted carefully:
OLD and NEW references are not available for table level triggers,
rather you can use them for record level triggers.
If you want to query the table in the same trigger, then you should use
the AFTER keyword, because triggers can query the table or change it
again only after the initial changes are applied and the table is back in a
consistent state.
Above trigger has been written in such a way that it will fire before any
DELETE or INSERT or UPDATE operation on the table, but you can write
your trigger on a single or multiple operations, for example BEFORE
DELETE, which will fire whenever a record will be deleted using DELETE
operation on the table.
Statement and Row Triggers
Example 1: Monitoring Statement Events
SQL> INSERT INTO dept (deptno, dname, loc)
2 VALUES (50, 'EDUCATION', 'NEW YORK');

Execute only once even if multiple rows affected


Example 2: Monitoring Row Events

SQL> UPDATE emp


2 SET sal = sal * 1.1
3 WHERE deptno = 30;

Execute for each row of the table affected by the event


Firing Sequence of Database
Triggers on a Single Row
DEPT table BEFORE statement trigger

DEPTNO DNAME LOC


10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS BEFORE row trigger
30 SALES CHICAGO AFTER row trigger
40 OPERATIONS BOSTON

AFTER statement trigger


Firing Sequence of Database
Triggers on Multiple Rows
EMP table

EMPNO ENAME DEPTNO BEFORE statement trigger


BEFORE row trigger
7839 KING 30
AFTER row trigger
7698 BLAKE 30 BEFORE row trigger
AFTER row trigger
7788 SMITH 30 BEFORE row trigger
AFTER row trigger

AFTER statement trigger


Trigger Execution order
1. Execute all BEFORE STATEMENT triggers
2. Disable temporarily all integrity constraints recorded against the table
3. Loop for each row in the table
Execute all BEFORE ROW triggers
Execute the SQL statement against the row and perform integrity constraint checking of the data
Execute all AFTER ROW triggers
4. Complete deferred integrity constraint checking against the table
5. Execute all AFTER STATEMENT triggers
INSTEAD OF Triggers
INSTEAD OF triggers provide a transparent way of modifying views that cannot be modified
directly through DML statements (INSERT, UPDATE, and DELETE). These triggers are called
INSTEAD OF triggers because, unlike other types of triggers, Oracle fires the trigger instead of
executing the triggering statement.
You can write normal INSERT, UPDATE, and DELETE statements against the view and the
INSTEAD OF trigger is fired to update the underlying tables appropriately. INSTEAD OF
triggers are activated for each row of the view that gets modified.
Modifying views can have ambiguous results:
Deleting a row in a view could either mean deleting it from the base table or updating
some values so that it is no longer selected by the view.
Inserting a row in a view could either mean inserting a new row into the base table or
updating an existing row so that it is projected by the view.
Updating a column in a view that involves joins might change the semantics of other
columns that are not projected by the view.
CREATE OR REPLACE TRIGGER manager_info_insert
INSTEAD OF INSERT ON manager_info

Instead of trigger REFERENCING NEW AS n


FOR EACH ROW
DECLARE
rowcnt number;
-- new manager information

BEGIN
CREATE OR REPLACE VIEW SELECT COUNT(*) INTO rowcnt FROM Emp_tab WHERE empno = :n.empno;
manager_info AS IF rowcnt = 0 THEN
INSERT INTO Emp_tab (empno,ename) VALUES (:n.empno, :n.ename);
ELSE
SELECT e.ename, e.empno, UPDATE Emp_tab SET Emp_tab.ename = :n.ename
d.dept_type, d.deptno, WHERE Emp_tab.empno = :n.empno;
p.prj_level, p.projno END IF;
SELECT COUNT(*) INTO rowcnt FROM Dept_tab WHERE deptno = :n.deptno;
FROM Emp_tab e, Dept_tab d, IF rowcnt = 0 THEN
Project_tab p INSERT INTO Dept_tab (deptno, dept_type)
VALUES(:n.deptno, :n.dept_type);
WHERE e.empno = d.mgr_no AND ELSE
d.deptno = p.resp_dept; UPDATE Dept_tab SET Dept_tab.dept_type = :n.dept_type
WHERE Dept_tab.deptno = :n.deptno;
END IF;
SELECT COUNT(*) INTO rowcnt FROM Project_tab
WHERE Project_tab.projno = :n.projno;
IF rowcnt = 0 THEN
INSERT INTO Project_tab (projno, prj_level)
VALUES(:n.projno, :n.prj_level);
ELSE
UPDATE Project_tab SET Project_tab.prj_level = :n.prj_level
WHERE Project_tab.projno = :n.projno;
END IF;
END;
Triggers on System Events and User
Events
You can use triggers to publish information about database events to subscribers.
Applications can subscribe to database events just as they subscribe to messages from other
applications. These database events can include:
System events
Database startup and shutdown
Data Guard role transitions
Server error message events
User events
User logon and logoff
DDL statements (CREATE, ALTER, and DROP)
DML statements (INSERT, DELETE, and UPDATE)
Triggers on system events can be defined at the database level or schema level. A trigger
defined at the database level fires for all users, and a trigger defined at the schema or table
level fires only when the triggering event involves that schema or table
System Events
System events that can fire triggers are related to instance startup and shutdown and error
messages. Triggers created on startup and shutdown events have to be associated with the
database. Triggers created on error events can be associated with the database or with a
schema.
STARTUP triggers fire when the database is opened by an instance. Their attributes include
the system event, instance number, and database name.
SHUTDOWN triggers fire just before the server starts shutting down an instance. You can
use these triggers to make subscribing applications shut down completely when the
database shuts down. For abnormal instance shutdown, these triggers cannot be fired. The
attributes of SHUTDOWN triggers include the system event, instance number, and database
name.
SERVERERROR triggers fire when a specified error occurs, or when any error occurs if no
error number is specified. Their attributes include the system event and error number.
DB_ROLE_CHANGE triggers fire when a role transition (failover or switchover) occurs in a
Data Guard configuration. The trigger notifies users when a role transition occurs, so that
client connections can be processed on the new primary database and applications can
continue to run.
User Events
User events that can fire triggers are related to user logon and logoff, DDL statements,
and DML statements.
LOGON and LOGOFF triggers can be associated with the database or with a schema.
Their attributes include the system event and user name, and they can specify simple
conditions on USERID and USERNAME.
LOGON triggers fire after a successful logon of a user.
LOGOFF triggers fire at the start of a user logoff.
System Triggers
CREATE OR REPLACE TRIGGER my_trigger
AFTER CREATE ON DATABASE
BEGIN
Do_Something;
END;

CREATE OR REPLACE TRIGGER On_Logon


AFTER LOGON
ON The_user.Schema
BEGIN
Do_Something;
END;
Controlling Triggers using SQL
Disable or Re-enable a database trigger
ALTER TRIGGER trigger_name DISABLE | ENABLE

Disable or Re-enable all triggers for a table


ALTER TABLE table_name DISABLE | ENABLE ALL TRIGGERS

Removing a trigger from the database


DROP TRIGGER trigger_name
Example: Calculating Derived Columns
CREATE OR REPLACE TRIGGER derive_commission_trg
BEFORE UPDATE OF sal ON emp
FOR EACH ROW
WHEN (new.job = 'SALESMAN')
BEGIN
:new.comm := :old.comm * (:new.sal/:old.sal);
END;
/
Example: Recording Changes
CREATE OR REPLACE TRIGGER audit_emp_values
AFTER DELETE OR UPDATE ON emp
FOR EACH ROW
BEGIN
INSERT INTO audit_emp_values (user_name,
timestamp, id, old_last_name, new_last_name,
old_title, new_title, old_salary, new_salary)
VALUES (USER, SYSDATE, :old.empno, :old.ename,
:new.ename, :old.job, :new.job,
:old.sal, :new.sal);
END;
/
Example: Protecting Referential Integrity
CREATE OR REPLACE TRIGGER cascade_updates
AFTER UPDATE OF deptno ON dept
FOR EACH ROW
BEGIN
UPDATE emp
SET emp.deptno = :new.deptno
WHERE emp.deptno = :old.deptno;
END
/
Restrictions for Database Triggers
Problem: impossible to determine certain values during execution of a sequence of
operations belonging to one and the same transaction
Mutating tables: contain rows which change their values after certain operation and
which are used again before the current transaction commits
Preventing table mutation:
Should not contain rows which are constrained by rows from other changing tables
Should not contain rows which are updated and read in one and the same
operation
Should not contain rows which are updated and read via other operations during
the same transaction
Example: Mutating Table
CREATE OR REPLACE TRIGGER emp_count
AFTER DELETE ON emp
FOR EACH ROW
DECLARE
num INTEGER;
BEGIN
SELECT COUNT(*) INTO num FROM emp;
DBMS_OUTPUT.PUT_LINE(' There are now ' || num || ' employees.');
END;
/

SQL> DELETE FROM emp


2 WHERE deptno = 30;

ERROR at line 1:
ORA-04091: table CGMA2.EMP is mutating, trigger/function may not see it
Example: Mutating Table (fixed)
CREATE OR REPLACE TRIGGER emp_count
AFTER DELETE ON emp
-- FOR EACH ROW
DECLARE
num INTEGER;
BEGIN
SELECT COUNT(*) INTO num FROM emp;
DBMS_OUTPUT.PUT_LINE(' There are now ' || num || ' employees.');
END;
/

SQL> DELETE FROM emp


2 WHERE deptno = 30;
There are now 8 employees.

6 rows deleted.
Rules for Good Practice
Rule 1: Do not change data in the primary key, foreign key, or
unique key columns of any table
Rule 2: Do not update records in the same table you read during
the same transaction
Rule 3: Do not aggregate over the same table you are updating
Rule 4: Do not read data from a table which is updated during
the same transaction
Rule 5: Do not use SQL DCL (Data Control Language) statements
in triggers

Вам также может понравиться