在MySQL的数据库开发中,常规的自增主键只能生成连续的数值,无法满足很多业务中复合主键的生成需求,比如需要主键包含日期、业务类型编码、递增序列的组合形式。BEFORE INSERT触发器可以在数据插入操作执行前自动触发,完成主键字段的赋值,完美适配这类复杂的主键生成场景。
BEFORE INSERT触发器的基础原理
BEFORE INSERT触发器会在INSERT语句执行之前被触发,此时新插入的记录行数据已经可以被访问,但是还没有写入到数据库表中。我们可以在触发器内部对新记录的字段进行修改,包括主键字段的赋值,修改后的数据会直接用于后续的插入操作。
触发器的创建语法如下:
CREATE TRIGGER 触发器名称
BEFORE INSERT ON 表名
FOR EACH ROW
BEGIN
-- 触发器逻辑代码
END;
其中NEW关键字用来指代即将插入的新记录,我们可以通过NEW.字段名的方式访问和修改新记录的字段值。
复合主键生成场景示例
假设我们有一个订单表order_info,需要生成格式为ORD-YYYYMMDD-XXXX的主键,其中ORD是固定业务前缀,YYYYMMDD是插入日期,XXXX是当日从0001开始的递增序列。
第一步:创建订单表
首先创建订单表结构,主键字段为order_id:
CREATE TABLE order_info (
order_id VARCHAR(20) PRIMARY KEY COMMENT '订单主键',
order_amount DECIMAL(10,2) COMMENT '订单金额',
create_time DATETIME COMMENT '创建时间'
) COMMENT '订单信息表';
第二步:创建序列辅助表
为了记录每日的订单序列,我们需要创建一个辅助表来存储日期和对应的当前序列值:
CREATE TABLE order_seq (
seq_date DATE PRIMARY KEY COMMENT '日期',
current_seq INT DEFAULT 1 COMMENT '当前序列值'
) COMMENT '订单序列辅助表';
第三步:创建BEFORE INSERT触发器
接下来创建BEFORE INSERT触发器,实现主键的自动生成逻辑:
DELIMITER //
CREATE TRIGGER trigger_gen_order_id
BEFORE INSERT ON order_info
FOR EACH ROW
BEGIN
DECLARE v_date_str VARCHAR(10);
DECLARE v_seq INT;
DECLARE v_seq_str VARCHAR(4);
-- 获取当前日期并格式化为YYYYMMDD
SET v_date_str = DATE_FORMAT(CURDATE(), '%Y%m%d');
-- 查询当日序列,如果不存在则插入初始记录
SELECT current_seq INTO v_seq FROM order_seq WHERE seq_date = CURDATE();
IF v_seq IS NULL THEN
INSERT INTO order_seq (seq_date, current_seq) VALUES (CURDATE(), 1);
SET v_seq = 1;
END IF;
-- 序列补零为4位
SET v_seq_str = LPAD(v_seq, 4, '0');
-- 拼接主键赋值给新记录的order_id字段
SET NEW.order_id = CONCAT('ORD-', v_date_str, '-', v_seq_str);
-- 同时自动设置创建时间
SET NEW.create_time = NOW();
-- 更新序列值,加1
UPDATE order_seq SET current_seq = current_seq + 1 WHERE seq_date = CURDATE();
END //
DELIMITER ;
第四步:测试触发器效果
我们执行插入语句,不需要指定order_id和create_time字段,验证触发器是否自动赋值:
INSERT INTO order_info (order_amount) VALUES (199.99); INSERT INTO order_info (order_amount) VALUES (299.50);
执行后查询order_info表,会看到两条记录的主键分别为ORD-20240520-0001和ORD-20240520-0002(日期部分根据实际插入日期变化),create_time也自动填充了当前时间。
使用注意事项
- 触发器中如果涉及多行SQL语句,需要使用
BEGIN...END包裹,并且临时修改语句结束符为//,避免和触发器内的分号冲突。 - 如果插入语句中手动指定了主键的值,BEFORE INSERT触发器仍然会执行,并且会覆盖手动指定的主键值和
create_time的值,因为触发器中对NEW.order_id和NEW.create_time做了赋值。 - 高并发场景下,上述序列辅助表的更新可能存在锁竞争问题,如果有高并发插入需求,可以考虑给
order_seq表的seq_date字段加唯一索引,同时使用INSERT ... ON DUPLICATE KEY UPDATE语句优化序列更新逻辑。 - 触发器逻辑不宜过于复杂,否则会影响插入操作的性能,复杂的生成逻辑可以考虑在业务层实现,或者拆分成多个简单的触发器。
适用场景总结
BEFORE INSERT触发器实现主键生成适合以下场景:主键生成规则和数据库强相关,不需要在多个业务系统中复用;希望减少业务层代码的复杂度,把生成逻辑下沉到数据库层;生成规则相对简单,不会对插入性能造成明显影响。如果主键生成规则需要跨系统复用,或者逻辑非常复杂,还是建议在业务层实现生成逻辑,更便于维护和扩展。
MySQL触发器BEFORE_INSERT主键生成自动赋值修改时间:2026-06-22 18:16:04