Advanced MySQL Features: Stored Procedures and Triggers
Explore advanced MySQL features like stored procedures (precompiled SQL code) and triggers (automatic actions triggered by database events).
Mastering MySQL Triggers
What are MySQL Triggers?
MySQL triggers are special stored programs that automatically execute in response to certain events on a particular table. These events can be INSERT, UPDATE, or DELETE operations. Triggers allow you to enforce business rules, maintain data integrity, and automate database tasks.
Triggers: Practical Examples
Understanding Trigger Components
A MySQL trigger consists of the following key components:
- Trigger Name: A unique identifier for the trigger.
- Trigger Time: Determines when the trigger is executed. Options are
BEFORE
andAFTER
. - Trigger Event: The operation that activates the trigger. Options are
INSERT
,UPDATE
, andDELETE
. - Trigger Table: The table the trigger is associated with.
- Trigger Body: The SQL code that is executed when the trigger is activated. This can be a single statement or a block of statements.
Syntax
CREATE TRIGGER trigger_name
trigger_time trigger_event
ON table_name
FOR EACH ROW
BEGIN
-- Trigger body (SQL statements)
END;
trigger_time
can be BEFORE
or AFTER
. trigger_event
can be INSERT
, UPDATE
, or DELETE
. FOR EACH ROW
specifies that the trigger will be executed for each row affected by the event.
NEW and OLD Keywords
Inside the trigger body, you can access the new and old versions of the row being affected. These are referenced using the NEW
and OLD
keywords.
- NEW: Refers to the new row being inserted or the new values of the row being updated. Available in
BEFORE INSERT
,AFTER INSERT
,BEFORE UPDATE
, andAFTER UPDATE
triggers. - OLD: Refers to the old row being deleted or the original values of the row being updated. Available in
BEFORE UPDATE
,AFTER UPDATE
,BEFORE DELETE
, andAFTER DELETE
triggers.
Real-World Examples of Triggers
Example 1: Automatically Updating Related Tables
Scenario: We have two tables: orders
and order_summary
. When a new order is placed (inserted into the orders
table), we want to update the order_summary
table to reflect the total number of orders and total revenue.
Table Structures:
CREATE TABLE orders (
order_id INT PRIMARY KEY AUTO_INCREMENT,
customer_id INT,
order_date DATE,
amount DECIMAL(10, 2)
);
CREATE TABLE order_summary (
total_orders INT DEFAULT 0,
total_revenue DECIMAL(20, 2) DEFAULT 0.00
);
-- Initial data in order_summary
INSERT INTO order_summary (total_orders, total_revenue) VALUES (0, 0.00);
Trigger Code:
CREATE TRIGGER after_order_insert
AFTER INSERT
ON orders
FOR EACH ROW
BEGIN
UPDATE order_summary
SET total_orders = total_orders + 1,
total_revenue = total_revenue + NEW.amount;
END;
Explanation:
- The trigger
after_order_insert
is activatedAFTER
anINSERT
operation on theorders
table. FOR EACH ROW
means the trigger executes for each row inserted into theorders
table.- The trigger body updates the
order_summary
table, incrementingtotal_orders
by 1 and adding theamount
from the new order (NEW.amount
) tototal_revenue
.
Testing:
INSERT INTO orders (customer_id, order_date, amount) VALUES (1, '2023-10-26', 100.00);
SELECT * FROM order_summary; -- Observe the updated values
Cleanup
DROP TRIGGER IF EXISTS after_order_insert;
Example 2: Preventing Invalid Data Entries
Scenario: We have a table employees
with a salary
column. We want to ensure that no employee's salary is ever set to a negative value.
Table Structure:
CREATE TABLE employees (
employee_id INT PRIMARY KEY AUTO_INCREMENT,
employee_name VARCHAR(255),
salary DECIMAL(10, 2)
);
Trigger Code:
CREATE TRIGGER before_employee_insert
BEFORE INSERT
ON employees
FOR EACH ROW
BEGIN
IF NEW.salary < 0 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Salary cannot be negative.';
END IF;
END;
CREATE TRIGGER before_employee_update
BEFORE UPDATE
ON employees
FOR EACH ROW
BEGIN
IF NEW.salary < 0 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Salary cannot be negative.';
END IF;
END;
Explanation:
- The trigger
before_employee_insert
is activatedBEFORE
anINSERT
operation on theemployees
table. We also need a similar trigger for UPDATE operations. FOR EACH ROW
means the trigger executes for each row being inserted/updated.- The trigger body checks if the new salary (
NEW.salary
) is less than 0. - If the salary is negative, the
SIGNAL SQLSTATE '45000'
statement raises a generic user-defined exception. TheSET MESSAGE_TEXT
provides a descriptive error message. This prevents the invalid data from being inserted or updated.
Testing:
-- Attempt to insert an employee with a negative salary
INSERT INTO employees (employee_name, salary) VALUES ('Test Employee', -100.00); -- This will result in an error
-- Attempt to update an existing employee's salary to a negative value
INSERT INTO employees (employee_name, salary) VALUES ('Existing Employee', 50000.00);
UPDATE employees SET salary = -200.00 WHERE employee_name = 'Existing Employee'; -- This will result in an error
Cleanup
DROP TRIGGER IF EXISTS before_employee_insert;
DROP TRIGGER IF EXISTS before_employee_update;
Example 3: Auditing Changes to Data
Scenario We want to track changes made to a product table, recording who made the change and when.
Table Structures
CREATE TABLE products (
product_id INT PRIMARY KEY AUTO_INCREMENT,
product_name VARCHAR(255),
price DECIMAL(10, 2),
last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
CREATE TABLE product_audit (
audit_id INT PRIMARY KEY AUTO_INCREMENT,
product_id INT,
old_product_name VARCHAR(255),
new_product_name VARCHAR(255),
old_price DECIMAL(10, 2),
new_price DECIMAL(10, 2),
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_by VARCHAR(255) -- To store the user who made the change (requires proper user authentication setup)
);
Trigger Code
CREATE TRIGGER after_product_update
AFTER UPDATE
ON products
FOR EACH ROW
BEGIN
INSERT INTO product_audit (
product_id,
old_product_name,
new_product_name,
old_price,
new_price,
updated_at,
updated_by
) VALUES (
OLD.product_id,
OLD.product_name,
NEW.product_name,
OLD.price,
NEW.price,
NOW(),
USER() -- Captures the current user's username
);
END;
Explanation:
- The trigger
after_product_update
fires after each update to theproducts
table. - It captures the old and new values of product attributes (name, price) along with the timestamp and the user who performed the update.
- The
USER()
function retrieves the MySQL username of the user who executed the update statement.
Testing:
-- Insert a sample product
INSERT INTO products (product_name, price) VALUES ('Widget', 19.99);
-- Update the product's price
UPDATE products SET price = 24.99 WHERE product_name = 'Widget';
-- Check the audit table
SELECT * FROM product_audit;
Cleanup
DROP TRIGGER IF EXISTS after_product_update;
Important Considerations When Using Triggers
- Performance: Triggers can impact performance, especially if they contain complex logic. Carefully design triggers to be as efficient as possible.
- Complexity: Overusing triggers can make a database schema difficult to understand and maintain.
- Debugging: Debugging triggers can be challenging. Use logging and careful testing to identify and resolve issues.
- Cascading Triggers: Be aware of the potential for cascading triggers (where one trigger activates another), which can lead to unexpected behavior.