Uploaded by 210107112

Triggers

advertisement
What are Triggers?
Triggers are special types of stored procedures in PL/SQL that are automatically executed in
response to certain events or changes in a database. These events could include data
manipulation language (DML) statements like INSERT, UPDATE, and DELETE or data definition
language (DDL) statements like CREATE, ALTER, and DROP.
Trigger Syntax
The syntax for creating a trigger in PL/SQL is as follows:
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
Trigger Execution Time
Triggers can be executed either before or after the triggering event occurs. A BEFORE trigger is
executed before the triggering event, while an AFTER trigger is executed after the triggering
event. An INSTEAD OF trigger is executed instead of the triggering event.
Trigger Event
The trigger event is the event that causes the trigger to execute. In PL/SQL, triggers can be
defined on INSERT, UPDATE, and DELETE events. These events are defined using the
BEFORE or AFTER keyword in the trigger definition.
Trigger Table
The trigger table is the table that the trigger is defined on. This is specified using the ON
keyword in the trigger definition.
OLD and NEW Pseudorecords
The OLD and NEW pseudorecords are used in the trigger code to access the old and new
values of the data being modified. The OLD pseudorecord contains the old values of the row
being modified, while the NEW pseudorecord contains the new values.
FOR EACH ROW
The FOR EACH ROW clause in the trigger definition specifies that the trigger is executed once
for each row affected by the triggering event.
SELECT trigger_name, table_name, trigger_type
FROM user_triggers;
ALTER TRIGGER TRIGGER_NAME DISABLE;
ALTER TRIGGER TRIGGER_NAME ENABLE;
BEFORE INSERT Trigger Example:
A BEFORE INSERT trigger is executed before a new row is inserted into the table. Here is an
example of a BEFORE INSERT trigger that validates the salary of a new employee:
CREATE OR REPLACE TRIGGER validate_salary
BEFORE INSERT ON oehr_employees
FOR EACH ROW
BEGIN
IF :NEW.salary < 5000 THEN
RAISE_APPLICATION_ERROR(-20001, 'Salary too low');
END IF;
END;
INSERT INTO OEHR_EMPLOYEES
(EMPLOYEE_ID,FIRST_NAME,LAST_NAME,EMAIL,PHONE_NUMBER,HIRE_DATE,JOB_ID,
SALARY,COMMISSION_PCT,MANAGER_ID,DEPARTMENT_ID) VALUES (669,
'Alexander','Hunold','AAHUNOLD’','590.423.4567','02/07/2012','IT_PROG',4990,NULL,102,50)
BEFORE UPDATE Trigger Example:
A BEFORE UPDATE trigger is executed before an existing row is updated. Here is an example
of a BEFORE UPDATE trigger that prevents updates to the hire_date column:
CREATE OR REPLACE TRIGGER prevent_hire_date_update
BEFORE UPDATE ON oehr_employees
FOR EACH ROW
BEGIN
IF UPDATING('hire_date') THEN
RAISE_APPLICATION_ERROR(-20001, 'Cannot update hire date');
END IF;
END;
UPDATE oehr_employees SET HIRE_DATE = '10/25/2012' WHERE EMPLOYEE_ID = 668;
BEFORE DELETE Trigger Example:
BEFORE DELETE trigger is executed before an existing row is deleted. Here is an example of a
BEFORE DELETE trigger that prevents the deletion of employees with a higher salary:
CREATE OR REPLACE TRIGGER prevent_high_salary_deletion
BEFORE DELETE ON oehr_employees
FOR EACH ROW
BEGIN
IF :OLD.salary > 20000 THEN
RAISE_APPLICATION_ERROR(-20001, 'Cannot delete employees with high salary');
END IF;
END;
DELETE FROM oehr_employees WHERE EMPLOYEE_ID = 100;
AFTER INSERT Trigger Example:
An AFTER INSERT trigger is executed after a new row is inserted into the table. Here is an
example of an AFTER INSERT trigger that updates the department manager of a newly added
employee:
CREATE OR REPLACE TRIGGER update_department_manager
AFTER INSERT ON oehr_employees
FOR EACH ROW
BEGIN
UPDATE oehr_departments
SET manager_id = :NEW.employee_id
WHERE department_id = :NEW.department_id;
END;
INSERT INTO OEHR_EMPLOYEES
(EMPLOYEE_ID,FIRST_NAME,LAST_NAME,EMAIL,PHONE_NUMBER,HIRE_DATE,JOB_ID,
SALARY,COMMISSION_PCT,MANAGER_ID,DEPARTMENT_ID) VALUES (777,
'Test','Test','Tessssst','590.423.4567','02/07/2012','IT_PROG',6000,NULL,102,50)
AFTER UPDATE Trigger Example:
An AFTER UPDATE trigger is executed after an existing row is updated. Here is an example of
an AFTER UPDATE trigger that inserts a row into an audit table for each update:
CREATE TABLE audit_table (
audit_id NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY,
audit_date TIMESTAMP DEFAULT SYSTIMESTAMP,
message VARCHAR2(200)
);
AFTER UPDATE ON oehr_employees
FOR EACH ROW
BEGIN
INSERT INTO audit_table(audit_date, message)
VALUES (SYSTIMESTAMP, 'Employee updated: ' || :OLD.first_name || ', ' ||
:OLD.employee_id);
END;
Update oehr_employees set first_name = 'Test1' where first_name = 'Test'
AFTER DELETE Trigger Example:
An AFTER DELETE trigger is executed after an existing row is deleted. Here is an example of
an AFTER DELETE trigger that updates the department manager to NULL when an employee
is deleted:
CREATE OR REPLACE TRIGGER update_manager_on_delete
AFTER DELETE ON oehr_employees
FOR EACH ROW
BEGIN
UPDATE oehr_departments
SET manager_id = NULL
WHERE manager_id = :OLD.employee_id;
END;
DELETE FROM oehr_employees WHERE EMPLOYEE_ID = 500;
"Before insert" trigger for the employees table that sets a default value for the salary
column:
CREATE OR REPLACE TRIGGER trg_set_default_salary
BEFORE INSERT
ON oehr_employees
FOR EACH ROW
DECLARE
BEGIN
IF :new.salary IS NULL THEN
:new.salary := 5000;
END IF;
END;
INSERT INTO oehr_employees
(EMPLOYEE_ID,FIRST_NAME,LAST_NAME,EMAIL,PHONE_NUMBER,HIRE_DATE,JOB_ID,
SALARY,COMMISSION_PCT,MANAGER_ID,DEPARTMENT_ID)
VALUES (668, 'John', 'Doe', 'SSSKING', '515.123.4568', '10/26/2011','AD_VP', NULL, NULL,
NULL, 90);
"Before insert" trigger for the jobs table that sets a default value for the max_salary
column:
CREATE OR REPLACE TRIGGER trg_set_default_max_salary
BEFORE INSERT
ON oehr_jobs
FOR EACH ROW
DECLARE
BEGIN
IF :new.max_salary IS NULL THEN
:new.max_salary := 20000;
END IF;
END;
INSERT INTO oehr_jobs (job_id, job_title, min_salary)
VALUES ('DEV', 'Developer', 5000);
"Before update" trigger for the jobs table that prevents updates to the min_salary column
that would result in a value less than 5000:
CREATE OR REPLACE TRIGGER trg_prevent_min_salary_update
BEFORE UPDATE OF min_salary
ON oehr_jobs
FOR EACH ROW
DECLARE
BEGIN
IF :new.min_salary < 3000 THEN
RAISE_APPLICATION_ERROR(-20001, 'Minimum salary cannot be less than 3000.');
END IF;
END;
UPDATE oehr_jobs
SET min_salary = 2500
WHERE job_id = 'AD_ASST';
"After update" trigger for the departments table that logs changes made to the
location_id column:
CREATE TABLE department_logs (
department_id NUMBER(10) NOT NULL,
old_location_id NUMBER(10) NOT NULL,
new_location_id NUMBER(10) NOT NULL,
changed_by VARCHAR2(50) NOT NULL
);
CREATE OR REPLACE TRIGGER trg_log_department_changes
AFTER UPDATE OF location_id
ON oehr_departments
FOR EACH ROW
DECLARE
BEGIN
INSERT INTO department_logs (department_id, old_location_id, new_location_id,
changed_by)
VALUES (:old.department_id, :old.location_id, :new.location_id, USER);
END;
UPDATE oehr_departments
SET location_id = 1700
WHERE department_id = 20;
Example before delete: jobs table
CREATE OR REPLACE TRIGGER prevent_job_delete
BEFORE DELETE ON oehr_jobs
FOR EACH ROW
DECLARE
employees_with_job INTEGER;
BEGIN
SELECT COUNT(*) INTO employees_with_job FROM oehr_employees WHERE job_id =
:OLD.job_id;
IF employees_with_job > 0 THEN
RAISE_APPLICATION_ERROR(-20001, 'This job cannot be deleted because it is currently
held by one or more employees.');
END IF;
END;
/
DELETE FROM oehr_jobs WHERE job_id = 'IT_PROG';
Example 1: employees table
Suppose you want to create a before update trigger for the employees table that prevents any
employee from being given a salary increase greater than 10% of their current salary. Here's
how you can do it:
CREATE OR REPLACE TRIGGER prevent_salary_increase
BEFORE UPDATE OF salary ON oehr_employees
FOR EACH ROW
BEGIN
IF :NEW.salary > (:OLD.salary * 1.1) THEN
RAISE_APPLICATION_ERROR(-20001, 'A salary increase of more than 10% is not
allowed.');
END IF;
END;
/
UPDATE oehr_employees SET salary = salary * 1.15 WHERE employee_id = 668;
Example 2: orders table
Suppose you want to create a before update trigger for the orders table that prevents any order
from being given a status of "shipped" if it is not already in "processing" status. Here's how you
can do it:
CREATE OR REPLACE TRIGGER prevent_status_change
BEFORE UPDATE OF ORDER_STATUS ON oehr_orders
FOR EACH ROW
BEGIN
IF :NEW.ORDER_STATUS = 2 AND :OLD.ORDER_STATUS != 1 THEN
RAISE_APPLICATION_ERROR(-20001, 'An order cannot be marked as 2 if it is not in 1
status.');
END IF;
END;
BEFORE INSERT
Example 1: employees table
Suppose you want to create a before insert trigger for the employees table that sets the hire
date to the current date if no hire date is provided. Here's how you can do it:
CREATE OR REPLACE TRIGGER set_hire_date
BEFORE INSERT ON employees
FOR EACH ROW
BEGIN
IF :NEW.hire_date IS NULL THEN
:NEW.hire_date := SYSDATE;
END IF;
END;
/
INSERT INTO employees (employee_id, first_name, last_name, email, job_id)
VALUES (99999, 'John', 'Doe', 'jdoe@example.com', 'IT_PROG');
Example 2: orders table
Suppose you want to create a before insert trigger for the orders table that sets the order date to
the current date if no order date is provided. Here's how you can do it:
CREATE OR REPLACE TRIGGER set_order_date
BEFORE INSERT ON orders
FOR EACH ROW
BEGIN
IF :NEW.order_date IS NULL THEN
:NEW.order_date := SYSDATE;
END IF;
END;
/
INSERT INTO orders (customer_id, order_total)
VALUES (12345, 100.00);
AFTER INSERT
Example 1: employees table
Suppose you want to create an after insert trigger for the employees table that adds a record to
the jobs table whenever a new job is added to the employees table. Here's how you can do it:
CREATE OR REPLACE TRIGGER add_job_to_jobs_table
CREATE OR REPLACE TRIGGER add_job_to_jobs_table
AFTER INSERT ON oehr_employees
FOR EACH ROW
BEGIN
INSERT INTO oehr_jobs (job_id, min_salary, max_salary)
VALUES (:NEW.job_id, :NEW.salary, :NEW.salary);
END;
INSERT INTO employees (employee_id, first_name, last_name, email, phone_number,
hire_date, job_id, salary, commission_pct, manager_id, department_id)
VALUES (1001, 'John', 'Doe', 'jdoe@example.com', '555-1234', SYSDATE, 'ANALYST', 5000,
NULL, 100, 50);
SELECT * FROM oehr_jobs WHERE min_salary = 5000
BEFORE DELETE
Example 3: jobs table
CREATE OR REPLACE TRIGGER prevent_job_delete
BEFORE DELETE ON oehr_jobs
FOR EACH ROW
DECLARE
employees_with_job INTEGER;
BEGIN
SELECT COUNT(*) INTO employees_with_job FROM oehr_employees WHERE job_id =
:OLD.job_id;
IF employees_with_job > 0 THEN
RAISE_APPLICATION_ERROR(-20001, 'This job cannot be deleted because it is currently
held by one or more employees.');
END IF;
END;
/
DELETE FROM oehr_jobs WHERE job_id = 'IT_PROG';
BEFORE UPDATE
Example 1: employees table
Suppose you want to create a before update trigger for the employees table that prevents any
employee from being given a salary increase greater than 10% of their current salary. Here's
how you can do it:
CREATE OR REPLACE TRIGGER prevent_salary_increase
BEFORE UPDATE OF salary ON oehr_employees
FOR EACH ROW
BEGIN
IF :NEW.salary > (:OLD.salary * 1.1) THEN
RAISE_APPLICATION_ERROR(-20001, 'A salary increase of more than 10% is not
allowed.');
END IF;
END;
/
UPDATE oehr_employees SET salary = salary * 1.15 WHERE employee_id = 668;
Example 2: orders table
Suppose you want to create a before update trigger for the orders table that prevents any order
from being given a status of "shipped" if it is not already in "processing" status. Here's how you
can do it:
CREATE OR REPLACE TRIGGER prevent_status_change
BEFORE UPDATE OF ORDER_STATUS ON oehr_orders
FOR EACH ROW
BEGIN
IF :NEW.ORDER_STATUS = 2 AND :OLD.ORDER_STATUS != 1 THEN
RAISE_APPLICATION_ERROR(-20001, 'An order cannot be marked as 2 if it is not in 1
status.');
END IF;
END;
/
UPDATE oehr_orders SET order_status = 2 WHERE order_id = 2458;
BEFORE INSERT
Example 1: employees table
Suppose you want to create a before insert trigger for the employees table that sets the hire
date to the current date if no hire date is provided. Here's how you can do it:
CREATE OR REPLACE TRIGGER set_hire_date
BEFORE INSERT ON employees
FOR EACH ROW
BEGIN
IF :NEW.hire_date IS NULL THEN
:NEW.hire_date := SYSDATE;
END IF;
END;
/
INSERT INTO employees (employee_id, first_name, last_name, email, job_id)
VALUES (99999, 'John', 'Doe', 'jdoe@example.com', 'IT_PROG');
Example 2: orders table
Suppose you want to create a before insert trigger for the orders table that sets the order date to
the current date if no order date is provided. Here's how you can do it:
CREATE OR REPLACE TRIGGER set_order_date
BEFORE INSERT ON orders
FOR EACH ROW
BEGIN
IF :NEW.order_date IS NULL THEN
:NEW.order_date := SYSDATE;
END IF;
END;
/
INSERT INTO orders (customer_id, order_total)
VALUES (12345, 100.00);
AFTER DELETE
Example 1: customers table
Suppose you want to create an after delete trigger for the customers table that removes any
orders associated with the deleted customer from the orders table. Here's how you can do it:
CREATE OR REPLACE TRIGGER remove_orders_for_customer
AFTER DELETE ON customers
FOR EACH ROW
BEGIN
DELETE FROM orders WHERE customer_id = :OLD.customer_id;
END;
/
DELETE FROM customers WHERE customer_id = 12345;
SELECT * FROM orders WHERE customer_id = 12345; -- should return no rows
Example 2: orders table
Suppose you want to create an after delete trigger for the orders table that updates the order
count for the department associated with the deleted order in the departments table. Here's how
you can do it:
CREATE OR REPLACE TRIGGER update_order_count
AFTER DELETE ON orders
FOR EACH ROW
BEGIN
UPDATE departments SET order_count = order_count - 1 WHERE department_id =
:OLD.department_id;
END;
/
DELETE FROM orders WHERE order_id = 1000;
SELECT * FROM departments WHERE department_id = 50; -- should show a decrease in
order_count
AFTER UPDATE
Example 1: employees table
Suppose you want to create an after update trigger for the employees table that updates the
salary of any employee who has been promoted. Here's how you can do it:
CREATE OR REPLACE TRIGGER update_salary_on_promotion
AFTER UPDATE ON employees
FOR EACH ROW
WHEN (NEW.job_id <> OLD.job_id AND NEW.salary > OLD.salary)
BEGIN
UPDATE employees SET salary = :NEW.salary WHERE employee_id = :OLD.employee_id;
END;
/
UPDATE employees SET job_id = 'MANAGER' WHERE employee_id = 100;
SELECT salary FROM employees WHERE employee_id = 100; -- should show an increase in
salary
Example 2: orders table
Suppose you want to create an after update trigger for the orders table that sends an email to
the customer whenever their order status changes. Here's how you can do it:
CREATE OR REPLACE TRIGGER send_email_on_order_status_change
AFTER UPDATE OF order_status ON oehr_orders
FOR EACH ROW
BEGIN
IF :NEW.customer_id IS NOT NULL THEN
dbms_output.put_line('Email sent to customer with ID ' || :NEW.customer_id);
END IF;
END;
/
UPDATE orders SET order_status = 'SHIPPED' WHERE order_id = 1000;
AFTER INSERT
Example 1: employees table
Suppose you want to create an after insert trigger for the employees table that adds a record to
the jobs table whenever a new job is added to the employees table. Here's how you can do it:
CREATE OR REPLACE TRIGGER add_job_to_jobs_table
CREATE OR REPLACE TRIGGER add_job_to_jobs_table
AFTER INSERT ON oehr_employees
FOR EACH ROW
BEGIN
INSERT INTO oehr_jobs (job_id, min_salary, max_salary)
VALUES (:NEW.job_id, :NEW.salary, :NEW.salary);
END;
INSERT INTO employees (employee_id, first_name, last_name, email, phone_number,
hire_date, job_id, salary, commission_pct, manager_id, department_id)
VALUES (1001, 'John', 'Doe', 'jdoe@example.com', '555-1234', SYSDATE, 'ANALYST', 5000,
NULL, 100, 50);
SELECT * FROM oehr_jobs WHERE min_salary = 5000
Download