MySQL生成列引用CURRENT_TIMESTAMP插入失败的差异问题排查
MySQL 8.0.24+ 生成列非空约束报错问题分析与解决
问题场景
表结构定义如下:
CREATE TABLE t3 ( `id` bigint NOT NULL AUTO_INCREMENT, `abc` bigint NOT NULL, `ts` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `createdday` int GENERATED ALWAYS AS (cast(`ts` as date)) STORED NOT NULL, PRIMARY KEY (`id`) );
在两个同版本(8.0.25)的MySQL实例中执行insert into t3 (abc) values (2);时,出现差异:
- 一个实例可成功写入,
createdday值符合预期; - 另一个实例报错:
ERROR 1048 (23000): Column 'createdday' cannot be null。
已确认两个实例explicit_defaults_for_timestamp配置均为OFF,且经多版本测试发现:
- MySQL 8.0.23中该插入语句可正常执行;
- 升级至8.0.24后开始出现非空错误,推测问题始于8.0.24版本。对应官方bug列表中的相关问题,但暂无修复更新。
原因分析
核心原因是MySQL 8.0.24对生成列的非空约束校验时机做了调整:
- 8.0.23及更早版本:生成列的计算逻辑在
ts列应用默认值(CURRENT_TIMESTAMP)之后执行,因此createdday能基于有效的ts值得到非空结果,通过非空校验; - 8.0.24+版本:非空约束校验被提前到生成列计算之前,此时
ts的默认值尚未被填充,校验逻辑判定createdday为NULL,触发非空约束错误。
即使是同版本的实例,也可能因初始化细节、临时参数变更等隐性差异出现表现不一致,但本质都是8.0.24引入的校验逻辑变更导致的问题。
解决方法
方案1:调整生成列表达式,兼容校验时机
修改生成列表达式,通过COALESCE函数确保即使ts在校验阶段被视为NULL,也能返回有效日期值:
ALTER TABLE t3 MODIFY COLUMN createdday int GENERATED ALWAYS AS (COALESCE(cast(`ts` as date), CURRENT_DATE)) STORED NOT NULL;
方案2:插入时显式指定ts列值
绕过默认值填充的时机问题,插入时直接指定ts的值:
INSERT INTO t3 (abc, ts) VALUES (2, CURRENT_TIMESTAMP);
方案3:回退至8.0.23版本(业务允许的情况下)
如果业务对版本兼容性要求不高,可暂时回退到MySQL 8.0.23,等待官方修复该bug。
方案4:将存储列改为虚拟列
虚拟生成列(VIRTUAL)的计算时机晚于存储列,能避开提前校验的问题:
ALTER TABLE t3 MODIFY COLUMN createdday int GENERATED ALWAYS AS (cast(`ts` as date)) VIRTUAL NOT NULL;
内容的提问来源于stack exchange,提问作者sillydong
相关产品推荐
相关产品推荐

