SQL 更多知识:触发器 Trigger

在数据库日常运维与开发中,我们常常需要在数据发生变更(插入、更新、删除)时,自动执行一些关联操作 —— 比如插入订单后自动更新库存、删除用户时同步清理其关联的订单数据、更新薪资时记录变更日志等。这些 “自动化响应” 的需求,正是触发器(Trigger) 要去解决的问题。本文将从触发器的基础概念出发,详解其作用、创建方法与触发逻辑,帮助你掌握触发器工具。

 

触发器的概念与作用

什么是触发器?

触发器是数据库中一种特殊的存储过程,它与数据表紧密关联,无需手动调用,当对表执行指定的数据库操作(INSERT/UPDATE/DELETE)时,会自动触发并执行预设的 SQL 逻辑。

简单来说,触发器就像给数据表装了一个 “自动感应开关”:当满足触发条件(如向订单表插入数据)时,开关自动打开,背后的预设操作(如扣减商品库存)会立刻执行,全程无需人工干预。

触发器的特性如下:

  • 触发时机:可指定在数据操作(DML)之前(BEFORE) 或之后(AFTER) 执行;

  • 触发事件:仅响应 INSERT、UPDATE、DELETE 三种 DML 操作;

  • 作用范围:触发器绑定到具体的数据表,一个触发器只能作用于一张表。

触发器的作用

触发器的作用在于实现数据操作的 “自动化关联处理”,主要应用场景包括:

(1)数据完整性约束补充数据库的约束能力,实现比主键、外键更复杂的业务规则。例如:限制订单金额不能为负数,若插入负数金额则自动修正或抛出异常。

(2)数据同步与联动:实现表间数据的自动同步。例如:插入新订单后,自动扣减商品表中对应商品的库存;删除用户时,自动删除其购物车、订单等关联数据。

(3)操作日志记录:自动记录数据变更轨迹,满足审计需求。例如:更新用户薪资后,自动将变更前的薪资、变更后的值、操作人、操作时间等信息写入薪资日志表。

(4)业务规则自动化:将固定的业务逻辑固化到数据库层,避免应用程序重复开发。例如:新增用户时,自动为其生成默认的收货地址记录。

注意:触发器虽能简化操作,但过度使用会增加数据库负担(如多层触发器嵌套可能导致性能下降),且故障排查难度较高,需结合业务场景合理设计。


创建触发器

触发器的创建语法因数据库(MySQL、Oracle、SQL Server)略有差异,本文以 MySQL 为例,讲解触发器的创建方法。

创建触发器的语法如下:

CREATE TRIGGER [触发器名]
[触发时机] [触发事件] ON [表名]
FOR EACH ROW  -- 行级触发器:每一行数据变更都会触发(MySQL默认行级)
BEGIN
    -- 触发器执行的SQL逻辑(可包含INSERT/UPDATE/DELETE等操作)
END;

语法说明:

  • 触发器名:建议按 “trg_表名_触发时机_触发事件” 命名(如 trg_order_after_insert),便于识别;

  • 触发时机:BEFORE(操作执行前触发)、AFTER(操作执行后触发);

  • 触发事件:INSERT(插入数据)、UPDATE(更新数据)、DELETE(删除数据);

  • FOR EACH ROW:表示行级触发器,即表中每一行数据发生变更时都会触发;

  • NEW/OLD 关键字:MySQL 中用于获取变更前后的数据:

    • INSERT 触发时:NEW 表示新增的行数据(如 NEW.order_id 表示新增订单的 ID);

    • UPDATE 触发时:OLD 表示更新前的行数据,NEW 表示更新后的行数据;

    • DELETE 触发时:OLD 表示被删除的行数据。

实战1:插入订单明细后自动扣减商品库存

由于订单上面没有存放商品信息,商品信息是通过其子表进行存放,因为一个订单可以存在多个商品。下面将对 order_product 表执行 INSERT 时调用触发器:

-- 创建触发器:trg_order_after_insert(订单表-插入后触发)
DELIMITER //  -- 临时修改语句结束符为//(避免BEGIN...END中的;提前终止语句)
CREATE TRIGGER trg_order_after_insert
AFTER INSERT ON `order_product`
FOR EACH ROW
BEGIN
    -- 扣减商品库存:NEW.product_id为新增订单的商品ID,NEW.quantity 为购买数量
    UPDATE `product` 
    SET stock = stock - NEW.quantity 
    WHERE product_id = NEW.product_id;
END //
DELIMITER ;  -- 恢复语句结束符为;

注意:订单表和商品表采用环境准备章节创建的表。

验证触发器效果:

-- 插入一个订单
> INSERT INTO `order`(order_id, user_id, status, total_amount, payment_method, payment_time, create_time, update_time)
VALUES(9998, 1, '待支付', 4999, '银行卡', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)

1 row(s) modified.

-- 查询订单对应商品的库存信息,目前还有 stock=1000 件库存
> select * from product p where p.product_id=1

product_id|name  |price  |stock|category_id|create_time        |update_time        |
----------+------+-------+-----+-----------+-------------------+-------------------+
         1|小米14手机|4999.00| 1000|          1|2025-01-05 00:25:00|2026-01-06 22:56:28|

1 row(s) fetched.

-- 为订单插入一条明细,数量为1
> INSERT INTO order_product(order_id, product_id, quantity, product_price, create_time, update_time)
VALUES(9998, 1, 1, 4999, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)

1 row(s) modified.

-- 再次验证商品,发现库存少了1个
> select * from product p where p.product_id=1

product_id|name  |price  |stock|category_id|create_time        |update_time        |
----------+------+-------+-----+-----------+-------------------+-------------------+
         1|小米14手机|4999.00|  999|          1|2025-01-05 00:25:00|2026-01-18 22:26:51|

1 row(s) fetched.

  

查看与删除触发器

查看触发器

下面以 MySQL 为例:

-- 查看当前数据库的所有触发器
SHOW TRIGGERS;

-- 查看指定触发器的创建语句(MySQL 8.0+支持)
SHOW CREATE TRIGGER trg_order_after_insert;

执行效果如下:

> SHOW TRIGGERS

Trigger               |Event |Table        |Statement                                                                                                                                                       |Timing|Created               |sql_mode                                                                                                                                 |Definer|character_set_client|collation_connection|Database Collation|
----------------------+------+-------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------+------+----------------------+-----------------------------------------------------------------------------------------------------------------------------------------+-------+--------------------+--------------------+------------------+
trg_order_after_insert|INSERT|order_product|BEGIN¶    -- 扣减商品库存:NEW.product_id为新增订单的商品ID,NEW.num为购买数量¶    UPDATE `product` ¶    SET stock = stock - NEW.quantity ¶    WHERE product_id = NEW.product_id;¶END|AFTER |2026-01-18 22:22:20.60|ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION|root@% |utf8mb4             |utf8mb4_general_ci  |utf8mb4_general_ci|

1 row(s) fetched.


> SHOW CREATE TRIGGER trg_order_after_insert

Trigger               |sql_mode                                                                                                                                 |SQL Original Statement                                                                                                                                                                                                                                         |character_set_client|collation_connection|Database Collation|Created              |
----------------------+-----------------------------------------------------------------------------------------------------------------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------+--------------------+------------------+---------------------+
trg_order_after_insert|ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION|CREATE DEFINER=`root`@`%` TRIGGER trg_order_after_insert¶AFTER INSERT ON `order_product`¶FOR EACH ROW¶BEGIN¶    -- 扣减商品库存:NEW.product_id为新增订单的商品ID,NEW.num为购买数量¶    UPDATE `product` ¶    SET stock = stock - NEW.quantity ¶    WHERE product_id = NEW.product_|utf8mb4             |utf8mb4_general_ci  |utf8mb4_general_ci|2026-01-18 22:22:20.0|

1 row(s) fetched.

删除触发器

删除触发器需要使用 DROP TRIGGER 语句,语法如下:

DROP TRIGGER [触发器名];

示例:删除订单触发器

> DROP TRIGGER trg_order_after_insert

0 row(s) modified.

 

注意事项

注意了,触发器虽然能实现自动化的数据操作,但它的 “隐蔽性” 和 “不可控性” 会给系统的维护、调试和性能带来一系列严重问题,因此在现代软件开发中通常被视为反模式(Anti-pattern)。

如果你不得不使用触发器,那么下面这些注意事项请务必小心:

(1)避免嵌套触发:触发器内部的 SQL 操作(如 UPDATE)可能触发其他触发器,多层嵌套易导致逻辑混乱或死循环,需严格控制。

(2)控制逻辑复杂度:触发器中应仅编写简单、核心的逻辑,复杂业务逻辑建议放在应用层实现,便于维护和排查问题。

(3) 注意性能影响:每行数据变更都会触发触发器,若表的 DML 操作频繁,复杂的触发器逻辑会显著降低数据库性能。

(4)数据一致性:触发器执行失败会导致原 DML 操作回滚(如插入订单时触发器扣减库存失败,订单插入也会失败),需做好异常处理。

(5)跨数据库兼容:不同数据库的触发器语法差异较大(如 Oracle 的触发器支持更多触发时机,SQL Server 的触发器语法结构不同),迁移时需适配调整。

  


说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
其他应用
公众号