mysql触发器需显式用now()赋值created_at/updated_at,字段类型与默认值须匹配;insert触发器只设created_at,update触发器须加条件判断避免误更新;timestamp默认值与触发器不可共存于同一字段。

MySQL 触发器本身不能直接给 created_at 或 updated_at 字段赋值为“当前时间戳”——除非你显式调用 NOW() 或 CURRENT_TIMESTAMP,且字段类型和默认值设置必须匹配,否则会报错或静默失败。
触发器里用 NOW() 还是 CURRENT_TIMESTAMP?
两者在大多数场景下行为一致,但有细微差别:
-
NOW()是函数调用,每次执行都重新求值,适合在 INSERT/UPDATE 中显式赋值 -
CURRENT_TIMESTAMP是常量表达式,在触发器中若作为默认值或直接赋值,MySQL 5.6+ 允许,但旧版本可能报错ERROR 1422: Explicit or implicit commit is not allowed - 更稳妥的做法统一用
NOW(),避免版本兼容问题
INSERT 触发器必须设 created_at,但别碰 updated_at
INSERT 时只应初始化创建时间,更新时间字段留空或设为 NULL(如果允许),避免干扰后续 UPDATE 逻辑:
CREATE TRIGGER t_before_insert_user
BEFORE INSERT ON user
FOR EACH ROW
BEGIN
IF NEW.created_at IS NULL THEN
SET NEW.created_at = NOW();
END IF;
IF NEW.updated_at IS NULL THEN
SET NEW.updated_at = NOW();
END IF;
END;
注意:上面写法看似省事,但会导致 INSERT 时 updated_at 和 created_at 相同——这没问题;但如果业务要求“首次插入后 updated_at 必须保持不变”,就得把 SET NEW.updated_at = NOW() 删掉,只设 created_at。
UPDATE 触发器只改 updated_at,且要避开无意义更新
UPDATE 触发器最常踩的坑是:哪怕只查不改、或字段值没变,也会触发并更新时间戳,导致误判“数据被修改”。解决方法是加条件判断:
- 用
IF NOT (OLD.col1 NEW.col1 AND OLD.col2 NEW.col2)检查是否有实质变更(可安全比较 NULL) - 或者更轻量:只对关键字段做判断,比如
IF OLD.name != NEW.name OR OLD.email != NEW.email - 不要用
OLD.* = NEW.*全字段比对,MySQL 不支持这种语法
示例:
CREATE TRIGGER t_before_update_user
BEFORE UPDATE ON user
FOR EACH ROW
BEGIN
IF OLD.name != NEW.name OR OLD.status != NEW.status THEN
SET NEW.updated_at = NOW();
END IF;
END;
TIMESTAMP 默认值和触发器共存时的冲突
如果你的字段定义是 updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,那再写 BEFORE UPDATE 触发器去设 NEW.updated_at 会失败,报错 ERROR 1362: Updating of <code>updated_at is not allowed in stored function or trigger。
所以必须二选一:
- 用触发器控制时间戳 → 字段定义为
TIMESTAMP NULL DEFAULT NULL或DATETIME DEFAULT NULL - 用
ON UPDATE CURRENT_TIMESTAMP自动更新 → 就别写 UPDATE 触发器了,否则冲突
混合使用(比如 created_at 用默认值,updated_at 用触发器)也行,但得确保字段类型一致、非空约束合理,否则 INSERT 时可能因 NULL 插入失败。
真正麻烦的不是写触发器,而是字段类型、NULL 性、默认值、自动更新属性四者之间的组合关系——漏掉任意一个,都会让时间戳“看起来设了,实际没生效”。











