触发器overview
触发器 (Trigger) is and 表关联 特殊store过程, 当表 on 发生specificevent (such asINSERT, UPDATE, DELETEoperation) 时, 会自动执行 SQL语句collection.
触发器 特点
- 自动执行: 当触发event发生时, 触发器会自动执行, 无需手动调用
- and 表关联: 触发器总 is and specific 表相关联
- event驱动: 由specific datalibraryevent触发, such asINSERT, UPDATE, DELETE
- transaction性: 触发器 执行 is transaction 一部分, 可以rollback
- 隐式执行: 触发器 in after 台隐式执行, for user透明
触发器 application场景
- dataverification: in data插入 or update before verificationdata has 效性
- dataintegrity: maintenance表之间 参照integrity
- audit跟踪: 记录data 变更history
- 自动计算: 自动计算fork值 or 汇总data
- synchronizationupdate: 当一个表 data变更时, 自动update相关表 data
- complex 业务规则: implementation complex 业务逻辑规则
触发器 creation
in MySQLin, usingCREATE TRIGGER语句creation触发器.
basic语法
CREATE TRIGGER trigger_name
{BEFORE | AFTER} {INSERT | UPDATE | DELETE}
ON table_name
FOR EACH ROW
BEGIN
-- 触发器code
END;
语法说明
trigger_name: 触发器 名称BEFORE | AFTER: 指定触发器 in event发生 before 还 is 发生 after 执行INSERT | UPDATE | DELETE: 指定触发event class型table_name: 触发器关联 表名FOR EACH ROW: 表示 for 每一行data都执行触发器
example: creationINSERT触发器
DELIMITER //
CREATE TRIGGER before_customer_insert
BEFORE INSERT ON customers
FOR EACH ROW
BEGIN
SET NEW.created_at = NOW();
SET NEW.updated_at = NOW();
END //
DELIMITER ;
example: creationUPDATE触发器
DELIMITER //
CREATE TRIGGER before_customer_update
BEFORE UPDATE ON customers
FOR EACH ROW
BEGIN
SET NEW.updated_at = NOW();
END //
DELIMITER ;
example: creationDELETE触发器
DELIMITER //
CREATE TRIGGER after_order_delete
AFTER DELETE ON orders
FOR EACH ROW
BEGIN
INSERT INTO order_history(order_id, customer_id, delete_date)
VALUES(OLD.id, OLD.customer_id, NOW());
END //
DELIMITER ;
触发器in 特殊variable
MySQL触发器inproviding了两个特殊variable, 用于访问触发 before after data:
NEW and OLDvariable
| variable | 适用场景 | describes |
|---|---|---|
| NEW | INSERT, UPDATE | 表示将要插入 or update after data |
| OLD | UPDATE, DELETE | 表示update before or delete before data |
usingexample
-- verificationupdateoperation
DELIMITER //
CREATE TRIGGER before_product_update
BEFORE UPDATE ON products
FOR EACH ROW
BEGIN
IF NEW.price < 0 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = '价格不能 for 负数';
END IF;
IF NEW.stock < 0 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'library存不能 for 负数';
END IF;
END //
DELIMITER ;
提示: in BEFORE触发器in, 可以modifyNEWvariable 值来更改将要插入 or update data; 而OLDvariable 值 is 只读 , 不能modify.
触发器 class型
根据触发时机 and eventclass型 不同, MySQL触发器可以分 for 以 under 几种class型:
按触发时机classification
- BEFORE触发器: in event发生 before 执行, 可以用于verificationdata or modify将要插入/update data
- AFTER触发器: in event发生 after 执行, 可以用于记录log, update关联表etc.operation
按eventclass型classification
- INSERT触发器: in 插入data时触发
- UPDATE触发器: in updatedata时触发
- DELETE触发器: in deletedata时触发
组合class型
| 触发器class型 | 触发时机 | 适用operation | 主要用途 |
|---|---|---|---|
| BEFORE INSERT | 插入 before | INSERT | dataverification, 默认值设置 |
| AFTER INSERT | 插入 after | INSERT | log记录, 关联表update |
| BEFORE UPDATE | update before | UPDATE | dataverification, 值modify |
| AFTER UPDATE | update after | UPDATE | log记录, 关联表update |
| BEFORE DELETE | delete before | DELETE | dataverification, 条件check |
| AFTER DELETE | delete after | DELETE | log记录, 关联表clean |
触发器 management
查看触发器
-- 查看所 has 触发器 SHOW TRIGGERS; -- 查看specific表 触发器 SHOW TRIGGERS LIKE 'table_name%'; -- from information_schemainquery触发器information SELECT * FROM information_schema.TRIGGERS WHERE TRIGGER_SCHEMA = 'database_name' AND EVENT_OBJECT_TABLE = 'table_name';
delete触发器
DROP TRIGGER IF EXISTS trigger_name;
modify触发器
MySQL不support直接modify触发器, 需要先delete再重 new creation:
-- delete old 触发器
DROP TRIGGER IF EXISTS trigger_name;
-- creation new 触发器
CREATE TRIGGER trigger_name
{BEFORE | AFTER} {INSERT | UPDATE | DELETE}
ON table_name
FOR EACH ROW
BEGIN
-- new 触发器code
END;
触发器 usingtechniques
dataverificationexample
-- verification订单数量
DELIMITER //
CREATE TRIGGER before_order_insert
BEFORE INSERT ON orders
FOR EACH ROW
BEGIN
IF NEW.quantity <= 0 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = '订单数量必须 big 于0';
END IF;
IF NEW.total_amount <= 0 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = '订单金额必须 big 于0';
END IF;
END //
DELIMITER ;
audit跟踪example
-- 记录产品价格变更
DELIMITER //
CREATE TRIGGER after_product_update
AFTER UPDATE ON products
FOR EACH ROW
BEGIN
IF OLD.price != NEW.price THEN
INSERT INTO price_history(product_id, old_price, new_price, change_date)
VALUES(OLD.id, OLD.price, NEW.price, NOW());
END IF;
END //
DELIMITER ;
级联updateexample
-- 当产品价格变更时, update订单详情in 价格
DELIMITER //
CREATE TRIGGER after_product_update
AFTER UPDATE ON products
FOR EACH ROW
BEGIN
IF OLD.price != NEW.price THEN
UPDATE order_details
SET unit_price = NEW.price
WHERE product_id = OLD.id
AND order_id IN (SELECT id FROM orders WHERE status = 'pending');
END IF;
END //
DELIMITER ;
触发器 限制 and Notes
触发器 限制
- 不能using某些语句: 触发器in不能usingSTART TRANSACTION, COMMIT, ROLLBACKetc.transaction控制语句
- performance影响: complex 触发器可能会影响datalibraryperformance, 特别 is in big 量dataoperation时
- 递归调用: 触发器不能递归调用, 可能会导致无限循环
- debug difficult : 触发器 执行 is 隐式 , debug比较 difficult
- portability差: 不同datalibrarysystem 触发器语法 and functions has 所不同
Notes
- 触发器应该保持简洁, 只package含必要 逻辑
- 避免 in 触发器in执行 complex query or big 量 计算
- using触发器时要考虑performance影响, 特别 is in high 频operation 表 on
- 确保触发器 逻辑正确, 避免意 out datamodify
- documentation化触发器 用途 and 逻辑, 便于 after 续maintenance
触发器 and store过程 区别
| features | 触发器 | store过程 |
|---|---|---|
| 执行方式 | 自动执行 | 手动调用 |
| 触发条件 | specificevent | 显式调用 |
| and 表 relationships | 必须 and 表关联 | 可以独立存 in |
| parameter传递 | usingNEW/OLDvariable | 显式parameter |
| return value | 无 | 可以 has return value |
触发器best practices
命名规范
- using has 意义 名称, such as
before_customer_insert - package含触发时机 (before/after) , 表名 and eventclass型 (insert/update/delete)
- using under 划线分隔单词, improving readable 性
performanceoptimization
- 保持触发器code简洁, 避免 complex 逻辑
- 避免 in 触发器in执行 big 量 datalibraryoperation
- 只 in 必要时using触发器, 考虑other替代solutions
- for 于频繁update 表, 谨慎using触发器
security性
- 确保触发器 逻辑正确, 避免data损 bad
- for 触发器for充分 test, 确保 in 各种circumstances under 都能正常工作
- 限制触发器 permission, 只授予必要 permission
可maintenance性
- for 触发器添加详细 comment, 说明其用途 and 逻辑
- documentation化所 has 触发器, including触发条件, 逻辑 and 影响
- 定期审查 and optimization触发器, 确保其仍然符合业务requirements
- 考虑usingversion控制management触发器code
实践case
case: library存management触发器
creation触发器来自动management产品library存, 当订单creation时reducinglibrary存, 当订单取消时restorelibrary存.
-- creation订单插入触发器
DELIMITER //
CREATE TRIGGER after_order_insert
AFTER INSERT ON order_details
FOR EACH ROW
BEGIN
UPDATE products
SET stock = stock - NEW.quantity
WHERE id = NEW.product_id;
END //
DELIMITER ;
-- creation订单delete触发器
DELIMITER //
CREATE TRIGGER after_order_delete
AFTER DELETE ON order_details
FOR EACH ROW
BEGIN
UPDATE products
SET stock = stock + OLD.quantity
WHERE id = OLD.product_id;
END //
DELIMITER ;
-- creation订单update触发器
DELIMITER //
CREATE TRIGGER after_order_update
AFTER UPDATE ON order_details
FOR EACH ROW
BEGIN
UPDATE products
SET stock = stock + OLD.quantity - NEW.quantity
WHERE id = OLD.product_id;
END //
DELIMITER ;
case: useroperationaudit
creation触发器来记录user login and operationhistory, implementationaudit跟踪functions.
-- creationuserloginaudit触发器
DELIMITER //
CREATE TRIGGER after_user_login
AFTER UPDATE ON users
FOR EACH ROW
BEGIN
IF OLD.last_login != NEW.last_login THEN
INSERT INTO user_audit(user_id, action, action_date, ip_address)
VALUES(OLD.id, 'login', NEW.last_login, NEW.last_ip);
END IF;
END //
DELIMITER ;
-- creationuserinformation变更audit触发器
DELIMITER //
CREATE TRIGGER before_user_update
BEFORE UPDATE ON users
FOR EACH ROW
BEGIN
IF OLD.email != NEW.email THEN
INSERT INTO user_audit(user_id, action, action_date, details)
VALUES(OLD.id, 'email_change', NOW(),
CONCAT(' from ', OLD.email, ' 改 for ', NEW.email));
END IF;
IF OLD.password != NEW.password THEN
INSERT INTO user_audit(user_id, action, action_date, details)
VALUES(OLD.id, 'password_change', NOW(), 'password已更改');
END IF;
END //
DELIMITER ;
互动练习
练习1: creationverification触发器
creation一个BEFORE INSERT触发器, verificationuserregister时 邮箱格式 is 否正确, 以及password long 度 is 否至 few for 6位.
DELIMITER //
CREATE TRIGGER before_user_insert
BEFORE INSERT ON users
FOR EACH ROW
BEGIN
-- verification邮箱格式
IF NEW.email NOT REGEXP '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$' THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = '邮箱格式不正确';
END IF;
-- verificationpassword long 度
IF LENGTH(NEW.password) < 6 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'password long 度至 few for 6位';
END IF;
-- 设置creation时间 and update时间
SET NEW.created_at = NOW();
SET NEW.updated_at = NOW();
END //
DELIMITER ;
练习2: creationaudit触发器
creation一个AFTER DELETE触发器, 当delete客户记录时, 将delete 客户information保存 to 客户history表in.
-- 首先creation客户history表
CREATE TABLE customer_history (
id INT AUTO_INCREMENT PRIMARY KEY,
customer_id INT,
name VARCHAR(50),
email VARCHAR(100),
phone VARCHAR(20),
delete_date DATETIME,
INDEX idx_customer_id (customer_id)
);
-- creationdelete触发器
DELIMITER //
CREATE TRIGGER after_customer_delete
AFTER DELETE ON customers
FOR EACH ROW
BEGIN
INSERT INTO customer_history(customer_id, name, email, phone, delete_date)
VALUES(OLD.id, OLD.name, OLD.email, OLD.phone, NOW());
END //
DELIMITER ;