mysql 5.7 的 datetime 不支持 on update current_timestamp:虽允许声明,但该子句被静默忽略,仅 default current_timestamp 有效;验证需用纯 sql 更新其他字段且不提及该列,否则 orm 显式赋值或空更新均导致失效。

MySQL 5.7 的 DATETIME 不支持 ON UPDATE CURRENT_TIMESTAMP
直接说结论:在 MySQL 5.7 中,DATETIME 字段可以设 DEFAULT CURRENT_TIMESTAMP,但 ON UPDATE CURRENT_TIMESTAMP 会被静默忽略——不是你写错了,是版本限制。执行 SHOW CREATE TABLE 看到该子句,不代表它生效。
- 5.6.5–7.9 版本只允许
DATETIME DEFAULT CURRENT_TIMESTAMP,ON UPDATE部分解析时被接受,运行时直接跳过,不报错也不更新 - 哪怕你用
ALTER TABLE ... MODIFY updated_at DATETIME ON UPDATE CURRENT_TIMESTAMP,结果也一样 - ORM(如 Laravel、MyBatis)生成的
UPDATE语句如果显式带上updated_at = ?(哪怕传的是NULL或旧值),MySQL 就认定“用户已赋值”,自动更新逻辑彻底失效
为什么 SHOW CREATE TABLE 显示了 ON UPDATE 却不触发?
这是 MySQL 5.7 的解析与执行分离导致的迷惑行为:建表或修改时语法校验通过,字段定义被存入元数据,但引擎层执行更新时发现类型不支持,就跳过逻辑,连 warning 都不抛。
- 验证是否真生效:用纯 SQL 执行
UPDATE t SET name='x' WHERE id=1(**完全不提updated_at字段**),再查值是否变 - 如果不变,基本可断定是版本限制;如果变,说明有其他干扰(比如触发器、ORM 强制赋值)
-
TIMESTAMP在 5.7 中完全支持ON UPDATE CURRENT_TIMESTAMP,但要注意时区转换和 2038 年限制
5.7 下想让 DATETIME 自动更新,只能绕开原生机制
没有“开启某个开关”就能让 5.7 的 DATETIME ON UPDATE 生效。替代方案只有两个方向:
- 改用
TIMESTAMP类型:建表时写updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,兼容性好,但注意它会按服务器时区转存、范围仅到 2038 年 - 用触发器兜底:
CREATE TRIGGER t_updated_at BEFORE UPDATE ON t FOR EACH ROW SET NEW.updated_at = CURRENT_TIMESTAMP,缺点是触发器逻辑独立于字段定义,维护成本高,且不能解决 INSERT 场景 - 业务层手动赋值:最可控,但容易漏,需统一 SDK 或 ORM 配置
别踩 NO_ZERO_DATE 导致的连带报错
MySQL 5.7 默认启用 NO_ZERO_DATE 和 NO_ZERO_IN_DATE,如果你的建表语句里混着 DEFAULT '0000-00-00 00:00:00' 这类零日期,哪怕没碰 ON UPDATE,也会先卡在 ERROR 1067 (42000): Invalid default value。
- 错误写法:
created_at DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00' DEFAULT CURRENT_TIMESTAMP—— 多个DEFAULT冲突,且零日期非法 - 正确写法二选一:
created_at DATETIME DEFAULT CURRENT_TIMESTAMP(允许 NULL),或created_at DATETIME NOT NULL DEFAULT '1000-01-01 00:00:00' - 临时改
sql_mode只对当前连接有效,生产环境必须改配置文件并重启 MySQL
ON UPDATE 就等于生效,结果上线后所有更新时间全靠代码补,还查不出原因**。版本限制这事没法绕,要么降级用 TIMESTAMP,要么升到 8.0+,或者接受业务层兜底。











