Triggers Creating Triggers (Statement level and Row LEVEL) Firing triggers (and EVENTS)
Trigger Example Assume 2 tables members (memberID, first, last, dob) memberspasswords (memberID, password) When a member is deleted, we would like to also delete the members password (first). CREATE TRIGGER deletePasswordOfDeletedMembers BEFORE DELETE ON members FOR EACH ROW DELETE FROM membersPasswords WHERE memberID = members.memberID
Trigger Syntax CREATE [DEFINER = { user | CURRENT_USER }] TRIGGER trigger_name trigger_time trigger_event ON tbl_name [FOR EACH ROW] [trigger_order] trigger_body trigger_time: { BEFORE | AFTER } trigger_event: { INSERT | UPDATE | DELETE } trigger_order: { FOLLOWS | PRECEDES } other_trigger_name
Statement (or Table) Level Trigger CREATE OR REPLACE TRIGGER someone_is_doing_updates AFTER UPDATE ON accounts BEGIN INSERT INTO UPDATE_LOG (EVENT,TIMESTAMP) VALUES (‘Someone is updating again!!!’,SYSDATE); END; This trigger would be fired once after the following DML statement is entered. UPDATE accounts SET balance = balance * 1.01
Row Level Trigger CREATE OR REPLACE TRIGGER log_updates AFTER UPDATE ON accounts FOR EACH ROW BEGIN INSERT INTO UPDATE_LOG (EVENT,TIMESTAMP) VALUES (‘Someone is updated an account’,SYSDATE); END; This trigger would be fired for every row updated in the following DML statement. UPDATE accounts SET balance = balance * 1.01
Consider the following SQL statement UPDATE accounts SET balance = balance * 1.01 Assume account has 1 million rows A statement-level trigger will be activated once. A row-level trigger will be activated a million times, once for every updated row.
Trigger to display changes in salary CREATE OR REPLACE TRIGGER display_salary_changes BEFORE DELETE OR INSERT OR UPDATE ON employees 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; ID Name Salary 1 Ramesh 2000 2 Khilan 1500 3 Komal 4500
Inserting – trigger’s display INSERT INTO employees (ID,NAME,SALARY) VALUES (4, 'Kriti', 7500.00 ); Old salary: New salary: 7500 Salary difference: ID Name Salary 1 Ramesh 2000 2 Khilan 1500 3 Komal 4500 4 Kriti 7500
Updating – trigger’s display UPDATE employees SET salary = salary + 500 WHERE id = 2; Old salary: 1500 New salary: 2000 Salary difference: 500 ID Name Salary 1 Ramesh 2000 2 Khilan 3 Komal 4500 4 Kriti 7500
Deleting – trigger’s display DELETE FROM employees WHERE id = 2; ID Name Salary 1 Ramish 2000 3 Komal 4500 4 Kriti 7500
Useful Triggers – Logging price history CREATE or REPLACE TRIGGER price_history_trigger BEFORE UPDATE OF price ON products FOR EACH ROW BEGIN INSERT INTO product_price_history VALUES (:old.product_id, :old.product_name, :old.supplier_name, :old.unit_price, SYSDATE); END;
CASCADING in a TRIGGER Trigger T1 - UPDATES a row to PRODUCTS_AVG_PRICES whenever a PRODUCTS price is UPDATED Trigger T2 - INSERTS a new row to table LOG_AVERAGE_PRICE_CHANGES whenever a PRODUCTS_AVG_PRICES is UPDATED. Here, an update to a PRODUCT’s price will cause trigger T1 to fire and update a row in PRODUCTS_AVG_PRICES, which will cause trigger T2 to fire and INSERT a row into LOG_AVERAGE_PRICE_CHANGES.
CYCLIC CASCADING in TRIGGERS Trigger Trigger12 – changes table Table2 whenever Table1 is changed Trigger Trigger23 – changes table Table3 whenever Table2 is changed Trigger Trigger31 – changes table Table1 whenever Table3 is changed If a change is made to Table1, that will cause a change to Table2 which will cause a change to Table3, which will cause a change to Table1 which will cause a change to Table 2, which will…. The database will eventually crash (no changes can be made) These would be called Recursive Triggers
Triggers vs. Foreign Keys Trigger can be used to verify data before entering it into a table. A Foreign Key check can be done in the using a BEFORE Trigger that checks if a value appears in a column of another table and only allow the INSERT to happen if so. MEMBERS(mid, first, last, password) TWEETS(mid, message, timestamp) We (ex: facebook) only want to enter tweets into the TWEETS table from actually users. However, if a user deletes their account after tweeting, we want the tweet to remain. Creating a FOREIGN KEY on TWEETS(mid) to MEMBERS(mid) will cause a member who has tweeted to not be able to be deleted. If we don’t want that, we can use a BEFORE trigger to check that the member exists before entering a tweet but leave the tweet around when the member is deleted.
Events (Triggers fired at a certain time) CREATE EVENT backup_DB ON SCHEDULE EVERY 1 HOUR STARTS CURRENT_TIMESTAMP + INTERVAL 1 MONTH ENDS CURRENT_TIMESTAMP + INTERVAL 1 MONTH + INTERVAL 1 WEEK DO ;
Events (ex: events during downtime) CREATE EVENT add_todays_members ON SCHEDULE EVERY 1 DAY STARTS '2017-11-27 03:20:00' ON COMPLETION PRESERVE ENABLE DO # disable indexes # add NEW_MEMBERS to MEMBERS # recalculate the indexes