MySQL中DEFAULT CURRENT_TIMESTAMP需结合版本、字段类型(TIMESTAMP而非DATETIME)、是否NOT NULL及ON UPDATE等条件使用,否则易报错或失效;推荐created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP。
MySQL 的 DEFAULT CURRENT_TIMESTAMP 在 DDL 脚本里怎么写才不会在生产出错
直接说结论:不能只靠建表语句里的 current_timestamp,得确认 mysql 版本、字段类型、是否允许 null、有没有显式指定 on update —— 这些都会让同一句 ddl 在不同环境行为不一致。
常见错误现象:ERROR 1067 (42000): Invalid default value for 'created_at',尤其在 MySQL 5.6 或开启严格模式的 5.7+ 环境;或者字段默认值写进去了,但上线后发现没生效,查表结构发现变成了 NULL。
-
TIMESTAMP字段才能用DEFAULT CURRENT_TIMESTAMP,DATETIME在 MySQL 5.6 不支持,默认值只能是常量(如'2020-01-01 00:00:00') - MySQL 5.6 要求第一个
TIMESTAMP字段自动补上DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,即使你没写 —— 这会导致脚本和实际结构对不上 - 如果字段定义为
created_at TIMESTAMP NULL,MySQL 会拒绝DEFAULT CURRENT_TIMESTAMP(除非关掉STRICT_TRANS_TABLES) - 推荐写法:
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,明确排除歧义
生成 DDL 脚本时如何避免本地和生产时间戳行为不一致
开发环境常是 MySQL 8.0+、宽松模式,生产可能是 5.7、严格模式 + sql_mode=STRICT_TRANS_TABLES,NO_ZERO_DATE —— 这会让带 CURRENT_TIMESTAMP 的 DDL 直接失败。
实操建议不是“统一升级版本”,而是让脚本本身兼容:
- 用
mysqldump --no-create-info --skip-triggers导出结构时,加--compatible=mysql40可强制降级语法(但会丢掉CURRENT_TIMESTAMP,不推荐) - 更可靠的是用
SHOW CREATE TABLE xxx在目标版本的测试库上执行,复制真实输出 —— 别信本地导出的 - 自动化生成脚本时,检查
SELECT @@sql_mode和SELECT VERSION(),动态拼接字段定义:5.6 用TIMESTAMP,8.0+ 可安全用DATETIME DEFAULT CURRENT_TIMESTAMP - 所有 DDL 必须在预发布环境跑一遍
mysql -u user -p -e "source xxx.sql",别跳过
ALTER TABLE 添加带默认时间戳的字段,为什么线上执行卡住或失败
不是语法问题,是锁和复制延迟导致的。加字段本身要重建表(除非是 MySQL 5.6+ 的 ALGORITHM=INPLACE),而 DEFAULT CURRENT_TIMESTAMP 在 ALTER 时会触发全表扫描初始化值 —— 尤其当表超大、又开了 binlog row 格式时,主从延迟飙升。
- 不要写
ADD COLUMN created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,改用两步:ADD COLUMN created_at TIMESTAMP→UPDATE table_name SET created_at = NOW() WHERE created_at IS NULL→ 再ALTER TABLE ... MODIFY created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP - 如果必须一步到位,确认 MySQL ≥ 8.0.13 且
innodb_file_per_table=ON,再加ALGORITHM=INSTANT(仅限加列、不改类型) - 线上执行前,先在从库停掉 SQL_THREAD,执行完再启,避免主从不一致
PostgreSQL 怎么对应处理?别直接套 MySQL 写法
PostgreSQL 没有 CURRENT_TIMESTAMP 作为列默认值的“隐式行为”,但也不等于能照搬。它要求默认值是稳定函数(stable),而 now() 是 volatile,所以直接写 DEFAULT now() 会报错。
- 正确写法是:
created_at TIMESTAMPTZ DEFAULT NOW()—— PostgreSQL 允许NOW()作默认值,虽然文档说 volatile,但这是特例 - 更稳妥的是用
CLOCK_TIMESTAMP(),它每次调用都返回真实时间(NOW()在事务内是固定的) - 注意:PostgreSQL 的
DEFAULT不会自动更新,要实现“创建+更新”双时间戳,得配TRIGGER,不能只靠 DDL - 迁移脚本里混用 MySQL 和 PG 的 DDL 时,务必用条件判断分发,别指望一个 SQL 文件通吃
时间戳默认值看着简单,但跨版本、跨引擎、跨环境时,最容易暴露的是对“默认值何时计算”“字段类型隐式转换”“SQL mode 实际影响”的误判。上线前拿生产配置跑一遍 EXPLAIN FORMAT=TREE 看执行计划,比读十遍文档管用。










