根本原因是mysql在update时显式赋值时间字段即禁用on update current_timestamp,即使show create table显示该属性;orm默认全字段更新、低版本datetime不支持、explicit_defaults_for_timestamp=on导致旧表隐式规则失效,均会静默跳过更新。

根本原因不是“没配对”,而是 MySQL 在执行 UPDATE 时主动跳过了自动更新逻辑——哪怕 SHOW CREATE TABLE 显示了 ON UPDATE CURRENT_TIMESTAMP,它也可能完全不触发。
UPDATE 语句里显式写了时间字段,自动更新就失效
这是最常踩的坑。ORM(如 Laravel、MyBatis)默认生成的 UPDATE 语句会把所有非主键字段都带上,比如:
UPDATE users SET name = 'Alice', updated_at = '2024-01-01 00:00:00' WHERE id = 1;
只要 updated_at 出现在 SET 子句中,MySQL 就视为“用户赋值”,直接禁用 ON UPDATE CURRENT_TIMESTAMP,连警告都不给。
- 验证方法:手动执行不含该字段的 UPDATE,例如
UPDATE users SET name = 'Alice' WHERE id = 1;,再查值是否变化 - 修复方向:在 ORM 中配置只更新变更字段,或改用数据库触发器兜底
- 注意:即使你传的是
NULL或旧值,也算“显式赋值”
DATETIME 类型在 MySQL 5.7 及更早版本不支持 ON UPDATE
很多人以为 DATETIME 和 TIMESTAMP 行为一致,其实不是:
- MySQL DATETIME DEFAULT CURRENT_TIMESTAMP 直接报错
ERROR 1067 (42000) - 5.6.5 ≤ MySQL DEFAULT CURRENT_TIMESTAMP,但
ON UPDATE CURRENT_TIMESTAMP被静默忽略 - MySQL 8.0+:
DATETIME才真正支持完整语法 - 而
TIMESTAMP从 5.6.5 起就支持两者,但受explicit_defaults_for_timestamp影响
如果你在 MySQL 5.7 上看到 updated_at DATETIME ON UPDATE CURRENT_TIMESTAMP 不生效,不是配置问题,是版本限制。
explicit_defaults_for_timestamp=ON 导致已有表“失忆”
MySQL 8.0 默认开启 explicit_defaults_for_timestamp=ON,但它只影响新表;已有表不会自动重写定义,但行为会悄悄变:
- 建表时没写
DEFAULT CURRENT_TIMESTAMP的TIMESTAMP字段,在旧版本(OFF)下会隐式获得默认值和ON UPDATE;切换到ON后,这些隐式规则全部失效 -
SHOW CREATE TABLE仍显示原有定义,但实际不执行 —— 这是“定义还在,逻辑已死” - 修复必须显式重定义列:
ALTER TABLE t MODIFY updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;
其他容易被忽略的硬性条件
即使语法和版本都对,以下任一条件不满足,ON UPDATE CURRENT_TIMESTAMP 也会静默失败:
-
TIMESTAMP字段必须是NOT NULL(除非你明确设了NULL并配了默认值,但极少见) - 存储引擎必须是
InnoDB;MyISAM对ON UPDATE支持不完整 - 服务器时区与客户端不一致时,可能造成时间值“看起来没变”,建议统一设为
+08:00或SYSTEM - 表上有
BEFORE UPDATE触发器且修改了该字段,会覆盖自动更新逻辑
真正麻烦的不是配置写不对,而是 MySQL 在多数失效场景下既不报错也不提示——它只是安静地跳过。验证前务必关掉 ORM,用纯 SQL 手动测,否则永远在猜。











