最稳妥的是使用timestamp类型配合current_timestamp()默认值及on update current_timestamp()自动更新;datetime虽5.6.5+支持但行为受限且依赖sql mode与时区配置。

建表时设 DEFAULT CURRENT_TIMESTAMP 最省心
只要字段类型是 TIMESTAMP 或带精度的 TIMESTAMP(3),建表时声明默认值就能一劳永逸:
CREATE TABLE logs ( id INT PRIMARY KEY AUTO_INCREMENT, msg VARCHAR(255), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );
插入时完全不用提这个字段:
INSERT INTO logs (msg) VALUES ('error occurred');
注意三点:
-
CURRENT_TIMESTAMP和NOW()在 MySQL 里等价,但CURRENT_TIMESTAMP更符合 SQL 标准,推荐写法 -
DATETIME类型也支持DEFAULT CURRENT_TIMESTAMP(MySQL 5.6.5+),但不自动处理时区转换 - 如果字段允许 NULL,又没设 DEFAULT,那它真会存成 NULL,不是当前时间
批量 INSERT 时省略时间字段才真正“自动”
批量插入多行,想让每行都带各自插入时刻,必须省略时间列名和值:
INSERT INTO logs (msg) VALUES ('first'), ('second'), ('third');
如果强行显式写 NOW():
INSERT INTO logs (msg, created_at) VALUES ('first', NOW()), ('second', NOW()), ('third', NOW());
看似一样,实则所有行的 created_at 值完全相同——因为 NOW() 在整条语句执行开始时只求值一次。容易误以为“每行时间不同”,这是典型陷阱。
真正需要每行独立时间戳(比如日志打点毫秒级差异),只能靠应用层生成或触发器,数据库原生机制做不到。
UPDATE 也要自动更新?加 ON UPDATE CURRENT_TIMESTAMP
常见需求是“创建时间不变,修改时间随每次 UPDATE 变”:
CREATE TABLE users ( id INT PRIMARY KEY, name VARCHAR(50), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP );
关键细节:
-
ON UPDATE CURRENT_TIMESTAMP只对TIMESTAMP和DATETIME有效,DATE不行 - MySQL 8.0+ 允许
TIMESTAMP列同时有DEFAULT CURRENT_TIMESTAMP和ON UPDATE CURRENT_TIMESTAMP;老版本要求至少一个非 NULL 约束 - 如果 UPDATE 语句里显式把
updated_at设为旧值(比如SET updated_at = '2020-01-01'),那它就不会被自动覆盖——这点常被忽略
跨数据库兼容性差,别硬套 MySQL 写法
不同数据库的时间函数和语法差异大,没法一套 SQL 走天下:
- PostgreSQL 用
CURRENT_TIMESTAMP,但默认带时区;要无时区用CURRENT_TIMESTAMP(0) - SQL Server 用
GETDATE(),且DATETIME2才支持默认值,DATETIME不支持 - Oracle 没有
CURRENT_TIMESTAMP默认值语法,得靠触发器或显式写SYSDATE
如果你的应用要换数据库,或者用 ORM 自动生成 SQL,优先考虑在应用层统一生成时间戳并传入——虽然多写一行代码,但可控、可测、不依赖方言。











