MySQL中用ON UPDATE CURRENT_TIMESTAMP一行即可实现自动更新,如updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,插入和更新时自动填充,无需触发器;但需避免设为NOT NULL DEFAULT '0000-00-00 00:00:00'导致失效。

MySQL 中用 ON UPDATE CURRENT_TIMESTAMP 就够了,别写触发器
MySQL 5.6.5+ 原生支持自动更新时间戳,updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP 一行搞定。插入时填入当前时间,后续每次 UPDATE 都自动刷新,无需额外逻辑。
常见错误是把 updated_at 设为 NOT NULL DEFAULT '0000-00-00 00:00:00' —— 这会导致 ON UPDATE 失效(MySQL 严格模式下直接报错)。必须设为 CURRENT_TIMESTAMP 或 NOW() 作为默认值。
-
TIMESTAMP类型也支持该语法,但注意其时区敏感性和 2038 年限制;DATETIME更稳妥 - 如果表已有数据,添加该字段时需先设为
NULL,再用ALTER TABLE ... MODIFY调整为NOT NULL,否则可能因隐式默认值冲突失败 - 手动
UPDATE时显式赋值(如SET updated_at = '2020-01-01')会覆盖自动行为 —— 这是设计使然,不是 bug
PostgreSQL 必须用触发器,DEFAULT 只管插入
PostgreSQL 的 DEFAULT CURRENT_TIMESTAMP 仅在 INSERT 时生效,UPDATE 不会自动刷新 updated_at。必须配触发器函数 + 触发器。
关键点:触发器类型必须是 BEFORE UPDATE,且函数里要写 NEW.updated_at = CURRENT_TIMESTAMP 并 RETURN NEW。用 AFTER 或漏掉 RETURN 都会导致字段不变。
- PostgreSQL 11+ 用
EXECUTE FUNCTION,10 及更早用EXECUTE PROCEDURE—— 混用会报错syntax error at or near "FUNCTION" - 函数语言必须是
plpgsql,不能用sql—— 因为NEW是行级变量,sql函数不支持 - 如果表有多个时间字段(如
created_at和updated_at),建议拆成两个独立触发器,避免逻辑耦合
SQL Server 触发器里必须用 inserted 表限定范围
SQL Server 触发器中,UPDATE 操作影响多行时,直接 UPDATE target_table SET updated_at = GETDATE() 会全表更新 —— 这是高频翻车点。
正确做法是关联 inserted 虚拟表,只更新本次被修改的行:WHERE id IN (SELECT id FROM inserted)。否则一次批量更新可能把所有历史记录的 updated_at 都刷成同一秒。
-
AFTER UPDATE最常用,但若需校验新值合法性(比如禁止status从active回退到draft),得用INSTEAD OF -
SET NOCOUNT ON推荐加上,避免触发器返回“X 行受影响”干扰应用层判断 - 如果表主键不是
INT或BIGINT(比如是UNIQUEIDENTIFIER),IN子句性能较差,应改用INNER JOIN写法
跨数据库迁移时,updated_at 字段定义最容易出兼容性问题
同一个建表语句,在 MySQL、PostgreSQL、SQL Server 上行为完全不同:MySQL 支持 ON UPDATE,PostgreSQL 完全忽略,SQL Server 直接语法报错。没有银弹方案。
真正需要跨库的项目,要么统一用 ORM 层(如 MyBatis Plus 的 @TableField(fill = FieldFill.UPDATE))处理,要么在各库分别实现对应机制 —— 别试图用一套 SQL 脚本打天下。
另外注意时区:MySQL 默认用系统时区,PostgreSQL 默认用 UTC,SQL Server 用服务器本地时区。如果业务要求统一东八区时间,PostgreSQL 触发器里得写 CURRENT_TIMESTAMP AT TIME ZONE 'Asia/Shanghai',而 SQL Server 得用 SYSDATETIMEOFFSET() 配合 AT TIME ZONE 转换。


















