Nov 19, 2012 ... Some concepts used in this lab will be discussed in class. In particular the use of
PL/SQL programming in the SQL*Plus window and the.
COSC 304 – Lab Assignment 7 Triggers Assigned: Monday November 19, 2012 Due: 2:00pm Friday November 30, 2012
NOTICE: Some concepts used in this lab will be discussed in class. In particular the use of PL/SQL programming in the SQL*Plus window and the display of error messages will be explained in class. Objective •
To learn how to create and use Database Triggers
Introduction A database trigger is a procedure that is attached to a table and executed in response to SQL INSERT, DELETE or UPDATE statements. The SQL statement, which causes the trigger to execute, is called a triggering event. When the trigger executes, we say that it fires. A trigger can fire before the triggering event or after the triggering event. It can fire once for each SQL statement (statement level trigger) or once for each row affected by the triggering event (row level trigger). A trigger is very similar to a stored procedure, except that it does not take parameters and is invoked implicitly by the triggering event. A trigger is created using the create trigger statement which has the following syntax: CREATE OR REPLACE TRIGGER trigger_name {BEFORE | AFTER} {DELETE | INSERT | UPDATE} [OF column_name] ON table_name [FOR EACH ROW] [WHEN condition ] pl/sql block BEFORE
causes Oracle to fire the trigger before executing the triggering event. For row triggers, this is a separate firing before each affected row is changed.
AFTER
causes Oracle to fire the trigger after executing the triggering event. For row triggers, this is a separate firing after each affected row is changed.
FOR EACH ROW
clause specifies the trigger to be a row trigger. If not specified, by default the trigger is a statement trigger.
COSC 304 – Lab 7 (Triggers)
Page 1
A statement trigger is fired once on behalf of the triggering statement, regardless of the number of rows in the table that the triggering statement affects (even if no rows are affected). For example, if a DELETE statement deletes several rows from a table, a statement-level DELETE trigger is fired only once, regardless of how many rows are deleted from the table. A row trigger is fired each time the table is affected by the triggering statement. For example, if an UPDATE statement updates multiple rows of a table, a row trigger is fired once for each row affected by the UPDATE statement. If a triggering statement affects no rows, a row trigger is not executed at all. WHEN
clause restricts the execution of the trigger. It fires only when the condition is met.
The PL/SQL code of a row-level trigger should not access the table to which the trigger is attached. If a row-level trigger is attached to, say the SUPPLIER table, then it cannot contain an SQL statement which selects rows from the SUPPLIER table. The reason is that the table is mutating and Oracle cannot provide a consistent view of the table. To resolve this problem, Oracle provides the before image of the row and modified image of the row. These images are accessed using the key words: OLD and NEW. For example, :OLD.ADDRESS refers to the supplier’s address before row modification took place and :NEW.ADDRESS refers to supplier’s address after the row has been modified.
Exercises In this part we will examine several examples of database triggers. You should do all the examples and make sure that the triggers work properly. This handout is stored on Moodle, so you can cut and paste the code from the examples into notepad, modify the data names to conform to names used in your schema, and then create your own triggers. Exercise 1 LAB7_ITEM (Product, Quantity, Price, Cost) is a table in which Cost is a computed column. It stores cost of the item, which is computed as Price times Quantity. Create LAB7_ITEM table: CREATE TABLE LAB7_ITEM (PRODUCT VARCHAR2(16), QUANTITY NUMBER, PRICE NUMBER, COST NUMBER); Insert the following row into LAB7_ITEM: INSERT INTO LAB7_ITEM(PRODUCT, QUANTITY, PRICE) VALUES (‘Winter Tire’, 4, 90.00); Now, retrieve the row from LAB7_ITEM: COSC 304 – Lab 7 (Triggers)
Page 2
SELECT * FROM LAB7_ITEM; The value of the Cost column is, as expected, missing. If we want the Cost column to be updated automatically, then either we have to provide suitable code in the user interface, or create a database trigger. Here is the statement to attach a trigger to LAB7_ITEM. CREATE OR REPLACE TRIGGER LAB7_ITEM_TRG BEFORE INSERT OR UPDATE ON LAB7_ITEM FOR EACH ROW BEGIN :NEW.COST := :NEW.QUANTITY * :NEW.PRICE; END; / The trigger LAB7_ITEM _TRG will fire once for each row affected by the insert or update operations. It will fire before the row is inserted or updated and the new value of the Cost column will be calculated as the Quantity times Price. Check if it works. Insert another row into LAB7_ITEM: INSERT INTO LAB7_ITEM(PRODUCT, QUANTITY, PRICE) VALUES ('All Season Tire’, 5, 105.50); Select all rows from LAB7_ITEM. Notice the value in the Cost column has been updated automatically for All Season Tires but not for Winter Tires. Update the price of Winter Tires to 119.85: UPDATE LAB7_ITEM SET PRICE = 119.85 WHERE PRODUCT LIKE ‘Winter%’; Select all the rows again to see that the cost of winter tires has been calculated. Exercise 2 Create a copy of the A4_SUP_PRO from Lab 4 and call it LAB7_SUP_PRO. Create a table LAB7_AUDIT which will be used to monitor all updates that increase the product price IN the LAB7_SUP_PRO table. CREATE TABLE LAB7_SUP_PRO AS SELECT * FROM ZAK_AKEN.A4_SUP_PRO;
COSC 304 – Lab 7 (Triggers)
Page 3
CREATE TABLE LAB7_AUDIT (DATE_ALTERED USER_ID PRO_ID SUP_ID OLD_PRICE NEW_PRICE
DATE, CHAR(8), NUMBER, NUMBER, NUMBER, NUMBER);
The following trigger fires when an update statement changes the price and the new price is greater than the old one. CREATE OR REPLACE TRIGGER LAB7_SUP_PRO_TRG BEFORE UPDATE ON LAB7_SUP_PRO FOR EACH ROW BEGIN IF (:NEW.PRICE > :OLD.PRICE) THEN INSERT INTO LAB7_AUDIT VALUES (SYSDATE, USER, :OLD.PRO_ID, :OLD.SUP_ID, :OLD.PRICE, :NEW.PRICE); END IF; END; See how it works: Increase all the prices from supplier number 53 by 20%: UPDATE LAB7_SUP_PRO SET PRICE = PRICE * 1.2 WHERE SUP_ID = 53; Now, select all the rows from TABLE LAB7_AUDIT table. The number of rows should be equal to the number of rows affected by your update statement. Exercise 3 Triggers can raise exceptions and prevent table modification. In this example you will create a trigger which will prevent unauthorised users from changing an employee’s salary. Create the LAB7_EMPLOYEES table by copying your A4_Employees table from Lab 4. CREATE OR REPLACE TRIGGER LAB7_EMP_TRG BEFORE UPDATE OF SALARY ON LAB7_EMPLOYEES FOR EACH ROW DECLARE UNAUTHORISED_USER EXCEPTION; BEGIN IF (USER 'ZAK_AKEN') and (USER 'FALL_2011') THEN RAISE UNAUTHORISED_USER; END IF; EXCEPTION WHEN UNAUTHORISED_USER THEN
COSC 304 – Lab 7 (Triggers)
Page 4
RAISE_APPLICATION_ERROR(-20001, 'ONLY MANAGER CAN CHANGE SALARY'); END;
This trigger uses the RAISE_APPLICATION_ERROR procedure, which takes two parameters: error number (any number between –20001 and –20999) and the error message to be displayed. To test your trigger you will need to co-operate with some other student in your class: grant update on your LAB7_EMPLOYEES table to your colleague. For example: GRANT UPDATE ON LAB7_EMPLOYEES TO ZAK_XXXX; Now, ask your classmate to change the salary in your employee table and see if the trigger fires. Examine the dictionary table USER_TRIGGERS, which stores the information on all the triggers in your schema. Here are some of the columns in this table TRIGGER_NAME TRIGGER_TYPE TRIGGERING_EVENT TABLE_OWNER TABLE_NAME COLUMN_NAME WHEN_CLAUSE STATUS ACTION_TYPE TRIGGER_BODY
VARCHAR2(30) VARCHAR2(16) VARCHAR2(75) VARCHAR2(30) VARCHAR2(30) VARCHAR2(4000) VARCHAR2(4000) VARCHAR2(8) VARCHAR2(11) LONG
The status of a trigger can be: ENABLED or DISABLED. Verify that all the triggers created in this lab are ENABLED. A trigger can have the setting DISABLE using the ALTER TRIGGER command. For example, to disable the LAB7_EMP_TRG trigger issue: ALTER TRIGGER LAB7_EMP_TRG DISABLE And then, enable it: ALTER TRIGGER LAB7_EMP_TRG ENABLE
COSC 304 – Lab 7 (Triggers)
Page 5
Assignment Exercise 4 Create table LAB7_GRADES with 5 columns: ID Number student id T1 Number mark from test 1 T2 Number mark from test 2 T3 Number mark from test 3 MARK Number average mark from test 1, 2 and 3. Create a trigger LAB7_GRADES_TRG that calculates the value of the MARK column. Exercise 5 The table LAB7_PRODUCTS has 2 columns, PRO_ID and PRICE. The table is subject to the Business Rule: Product price cannot be changed (increased or decreased) by more than 15 percent. Name ---------PRO_ID PRICE
Null ------------NOT NULL NOT NULL
Type -------------NUMBER NUMBER
Create the LAB7_PRODUCTS table as indicated above. Add a primary key constraint to make sure that there is only one row for each product. Add a check constraint that will ensure that the price is always greater than zero. Create the LAB7_PRO_TRG trigger that raises the application error 'Price can not be changed by more than 15%’ when an update statement tries to change price by more than 15 percent. Insert suitable rows into the LAB7_ PRODUCTS table and test your trigger. (HINT: Use an IF statement explained in class) Exercise 6 Create 2 tables, LAB7_RESERVATIONS table with 2 columns: Name ---------FLIGHT_ID CUSTOMER_PHONE
Null ------------NOT NULL NOT NULL
Type --------------CHAR(6) NUMBER
and LAB7_FLIGHTS table with 2 columns: FLIGHT_ID SEATS
NOT NULL NOT NULL
CHAR(6) NUMBER
Make FLIGHT_ID the primary key in the LAB7_FLIGHTS table. Insert the following rows into LAB7_FLIGHTS:
COSC 304 – Lab 7 (Triggers)
Page 6
FLIGHT_ID SEATS AC0529 120 AC0530 0 Create a trigger LAB7_RES_TRG that will ensure that when a new row is inserted into the LAB7_RESERVATIONS table, the flight id is in the LAB7_FLIGHTS table and that the number of seats on this flight, SEATS is greater than 0. Here are the details of how the trigger should behave:
If flight id is not in the flights table it should raise application error ‘Invalid flight id’
If flight id is in the flights table, (for example AC0529) but SEATS = 0, then it should raise application error ‘Flight AC0529 has no seats left’
If flight id is in the flights table and SEATS > 0, then it should update the appropriate row in flights table by setting SEATS = SEATS – 1 for this flight.
(Hint: Use the PL/SQL statement select .. into .. ) Marks Exercises 1, 2, 3 and 4 – two points each Exercises 5 and 6 – three points each Exercise 7 – 6 points Total: 20 points.
COSC 304 – Lab 7 (Triggers)
Page 7