MySQL触发器

掌握触发器的创建、使用和管理

什么是触发器?

触发器是数据库中的一种特殊存储过程,它在特定事件(INSERT、UPDATE、DELETE)发生时自动执行。触发器与表相关联,当表发生数据变更时自动触发。

触发器的特点

  • 自动执行,无需手动调用
  • 与特定表关联
  • 在数据变更前或后执行
  • 可以访问变更前后的数据
  • 用于实现数据完整性、审计、日志记录等

创建触发器

基本语法

SQL示例
CREATE TRIGGER trigger_name
trigger_time trigger_event
ON table_name FOR EACH ROW
BEGIN
    -- 触发器逻辑
END;

触发时间

  • BEFORE - 在事件执行前触发
  • AFTER - 在事件执行后触发

触发事件

  • INSERT - 插入数据时触发
  • UPDATE - 更新数据时触发
  • DELETE - 删除数据时触发

触发器示例

BEFORE INSERT 触发器

SQL示例
CREATE TRIGGER before_employee_insert
BEFORE INSERT ON employees FOR EACH ROW
BEGIN
    SET NEW.created_at = NOW();
    SET NEW.updated_at = NOW();
END;

AFTER INSERT 触发器

SQL示例
CREATE TRIGGER after_employee_insert
AFTER INSERT ON employees FOR EACH ROW
BEGIN
    INSERT INTO audit_log (action, table_name, record_id, changed_at)
    VALUES ('INSERT', 'employees', NEW.employee_id, NOW());
END;

BEFORE UPDATE 触发器

SQL示例
CREATE TRIGGER before_employee_update
BEFORE UPDATE ON employees FOR EACH ROW
BEGIN
    SET NEW.updated_at = NOW();
    
    -- 记录薪资变更
    IF NEW.salary != OLD.salary THEN
        INSERT INTO salary_history (employee_id, old_salary, new_salary, changed_at)
        VALUES (NEW.employee_id, OLD.salary, NEW.salary, NOW());
    END IF;
END;

AFTER DELETE 触发器

SQL示例
CREATE TRIGGER after_employee_delete
AFTER DELETE ON employees FOR EACH ROW
BEGIN
    INSERT INTO deleted_employees (employee_id, first_name, last_name, deleted_at)
    VALUES (OLD.employee_id, OLD.first_name, OLD.last_name, NOW());
END;

OLD和NEW关键字

在触发器中,可以使用OLD和NEW关键字来访问变更前后的数据:

触发器类型OLDNEW
INSERTNULL要插入的新数据
UPDATE更新前的旧数据更新后的新数据
DELETE删除前的旧数据NULL

使用OLD和NEW的示例

SQL示例
CREATE TRIGGER prevent_salary_decrease
BEFORE UPDATE ON employees FOR EACH ROW
BEGIN
    IF NEW.salary < OLD.salary THEN
        SIGNAL SQLSTATE '45000' 
        SET MESSAGE_TEXT = '薪资不能降低';
    END IF;
END;

多个触发器的执行顺序

MySQL 5.7+允许在同一表、同一事件上定义多个触发器,使用FOLLOWS/PRECEDES指定顺序:

SQL示例
CREATE TRIGGER trigger1 BEFORE INSERT ON t FOR EACH ROW ...
CREATE TRIGGER trigger2 BEFORE INSERT ON t FOR EACH ROW FOLLOWS trigger1 ...

触发器中调用存储过程和函数

SQL示例
CREATE TRIGGER call_proc_example
AFTER INSERT ON orders FOR EACH ROW
BEGIN
    CALL update_summary(NEW.product_id, NEW.quantity);
END;

管理触发器

查看触发器

SQL示例
-- 查看所有触发器
SHOW TRIGGERS;

-- 查看特定触发器的定义
SHOW CREATE TRIGGER trigger_name;

删除触发器

SQL示例
DROP TRIGGER IF EXISTS trigger_name;

触发器的应用场景

数据完整性

SQL示例
CREATE TRIGGER validate_email_format
BEFORE INSERT ON users FOR EACH ROW
BEGIN
    IF NEW.email NOT LIKE '%@%.%' THEN
        SIGNAL SQLSTATE '45000' 
        SET MESSAGE_TEXT = '邮箱格式不正确';
    END IF;
END;

审计日志

SQL示例
CREATE TRIGGER audit_customer_changes
AFTER UPDATE ON customers FOR EACH ROW
BEGIN
    IF NEW.name != OLD.name THEN
        INSERT INTO customer_audit (customer_id, field_name, old_value, new_value, changed_at)
        VALUES (NEW.customer_id, 'name', OLD.name, NEW.name, NOW());
    END IF;
    
    IF NEW.email != OLD.email THEN
        INSERT INTO customer_audit (customer_id, field_name, old_value, new_value, changed_at)
        VALUES (NEW.customer_id, 'email', OLD.email, NEW.email, NOW());
    END IF;
END;

数据同步

SQL示例
CREATE TRIGGER sync_inventory
AFTER INSERT ON orders FOR EACH ROW
BEGIN
    UPDATE products 
    SET stock_quantity = stock_quantity - NEW.quantity
    WHERE product_id = NEW.product_id;
END;

自动生成业务编号

SQL示例
CREATE TRIGGER generate_order_number
BEFORE INSERT ON orders FOR EACH ROW
BEGIN
    SET NEW.order_number = CONCAT('ORD-', DATE_FORMAT(NOW(), '%Y%m%d'), LPAD(NEW.id, 5, '0'));
END;

触发器的限制和注意事项

  • 触发器不能对已触发表进行修改,可能导致递归(除非设置合理的逻辑)
  • 触发器中的错误会导致整个事务回滚
  • 触发器不能返回结果集(不能使用SELECT ... INTO)
  • 不能使用事务显式控制语句(COMMIT/ROLLBACK)
  • 触发器内的语句不直接受自动提交影响,而是随触发事务提交或回滚

触发器与复制

在主从复制环境中,触发器行为取决于设置:

  • 通常在从库上会执行触发器(除非设置replicate_wild_do_table等)
  • 可能导致主从数据不一致,需要谨慎设计
  • 建议在复制环境中测试触发器的行为

触发器元数据查询与监控

SQL示例
-- 查询所有触发器及详细信息
SELECT TRIGGER_NAME, EVENT_MANIPULATION, EVENT_OBJECT_TABLE, ACTION_TIMING
FROM information_schema.TRIGGERS
WHERE TRIGGER_SCHEMA = 'your_database';

触发器性能考虑

使用触发器时需要考虑以下性能因素:

  • 触发器会增加数据库负载
  • 复杂的触发器逻辑可能影响性能
  • 触发器中的错误可能导致事务回滚
  • 避免在触发器中执行耗时操作
  • 批量操作时,触发器每行执行一次,效率较低

触发器最佳实践

  • 保持触发器逻辑简单
  • 避免在触发器中执行复杂查询
  • 注意触发器的执行顺序
  • 测试触发器在各种情况下的行为
  • 记录触发器的用途和逻辑
  • 尽量避免使用递归触发器(除非必要且可控)
  • 定期审查触发器是否仍然必要
注意事项:触发器虽然功能强大,但过度使用会影响数据库性能。建议只在必要时使用触发器,并确保触发器逻辑简单高效。

下一步学习

掌握了触发器知识后,您可以继续学习: