Mysql中 Invalid ON UPDATE clause for 'update_time' column表创建失败

本文解释了在从高版本MySQL向低版本迁移数据时遇到的更新时间戳字段错误。详细介绍了ONUPDATECURRENT_TIMESTAMP属性如何导致问题,尤其是在MySQL5.5中。提供了解决方案,即移除此属性避免错误。
序言

会有这个错误,首先要介绍下,当执行update操作是,并且字段有ON UPDATE CURRENT_TIMESTAMP属性。则字段无论值有没有变化,它的值也会跟着更新为当前UPDATE操作时的时间。从高版本的mysql导数据到低版本往往会有这个错误,MySQL 5.5 每个表只允许一个列的默认值根据时间戳生成时间 。去掉即可。

create table 'test' (
'id' bigint(30) unsigned NOT NULL AUTO_INCREMENT COMMENT '组合id',
'update_time'   datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新日期'
)
<think>我们正在处理两个主要问题:用户原始问题中的1064错误,以及后续对话中提到的1067错误。在引用[1]和[2]中,用户遇到了1067错误,这通常与时间戳字段的默认值无效有关。从用户的问题描述中,他们尝试在`update_time`字段上使用`datetimeDEFAULTCURRENT_TIMESTAMP`,但出现了错误。分析:1.在MySQL中,`datetime`类型字段在5.6版本之前是不允许使用`CURRENT_TIMESTAMP`作为默认值的(除非该字段是`timestamp`类型)。但在5.6及以上版本,允许`datetime`类型有一个默认值为`CURRENT_TIMESTAMP`,但一张最多只能有一个这样的字段(包括`timestamp`类型的自动更新字段)。2.然而,错误1067提示“Invaliddefaultvalue”往往是因为默认值设置不正确,特别是当尝试使用`0000-00-0000:00:00`这样的值,而SQL模式中包含了`NO_ZERO_DATE`(这是默认包含在`sql_mode`中的)时。从引用[1]中,我们看到的结构中:`create_timetimestampNOTNULLDEFAULT'0000-00-0000:00:00'``update_timetimestampNOTNULLDEFAULTCURRENT_TIMESTAMPONUPDATECURRENT_TIMESTAMP`这里有两个问题:a)在较新的MySQL版本中,不允许使用'0000-00-0000:00:00'作为默认值(因为NO_ZERO_DATE模式)。b)在同一个中,不能有两个timestamp字段同时设置默认值(除非其中一个允许为NULL,或者使用MySQL的特定版本,但通常不允许两个字段都有默认值,其中一个还使用了自动更新)。但是,现在用户的问题是关于1067错误,且指定在`update_timedatetimeDEFAULTCURRENT_TIMESTAMP`上。解决方案:1.如果用户使用的是5.6.5之前的版本,那么`datetime`类型字段不能使用`CURRENT_TIMESTAMP`作为默认值。所以,如果想记录时间戳,并且需要使用自动更新,应该将字段类型改为`TIMESTAMP`。2.如果用户使用的是5.6.5或更高版本,那么`datetime`类型可以设置默认值为`CURRENT_TIMESTAMP`,但需要注意以下规则:-每个最多只能有一个`TIMESTAMP`或`DATETIME`字段可以设置默认值为`CURRENT_TIMESTAMP`或者`ONUPDATECURRENT_TIMESTAMP`。-但是,如果用户已经有一个字段设置了`DEFAULTCURRENT_TIMESTAMP`,再设置第二个就会出现问题。在用户给出的例子中,引用[1]的建语句就有两个`timestamp`字段,并且都给定了默认值,其中一个还使用了`ONUPDATE`。这会导致错误(在严格模式下),因为MySQL在一个中只允许一个`TIMESTAMP`字段使用`CURRENT_TIMESTAMP`作为默认值(除非启用特定的配置)。然而,在MySQL5.6.5及更高版本,这个限制有所放宽:允许任意一个字段使用`CURRENT_TIMESTAMP`作为默认值,并且另一个使用`ONUPDATECURRENT_TIMESTAMP`,但是不允许两个字段都同时设置`DEFAULTCURRENT_TIMESTAMP`(除非它们同时设置`ONUPDATE`,但这样也是不允许的,因为MySQL要求每个只能有一个具有自动初始化或自动更新特性的字段?)。实际上,在5.6.5版本后,这个限制被取消了,可以有一列是`DEFAULTCURRENT_TIMESTAMP`,另一列是`ONUPDATECURRENT_TIMESTAMP`,甚至可以有一列同时具备两者。因此,如果用户仍然遇到1067错误,需要检查:-MySQL版本:是否低于5.6.5?如果是,则不能将`datetime`字段设为`DEFAULTCURRENT_TIMESTAMP`。-是否在同一个中设置了多个字段使用`CURRENT_TIMESTAMP`?如果是,确保满足版本限制。-检查SQL模式(sql_mode)是否包含了`NO_ZERO_DATE`和`NO_ZERO_IN_DATE`。因为如果字段类型是`timestamp`,并且设置默认值为0(即0000-00-0000:00:00),在严格模式下是不允许的。可以通过修改sql_mode来去掉这两个选项(如引用[3]),但这通常不推荐,因为会降低数据完整性。针对用户当前的具体错误(update_time字段的默认值无效),我们考虑以下两种情况的解决方案:情况1:用户使用`datetime`类型并且想使用`CURRENT_TIMESTAMP`作为默认值,但MySQL版本低于5.6.5。解决方案:将字段改为`timestamp`类型(但注意,timestamp的取值范围是1970-2038年,而datetime是1000-9999年)。情况2:用户的SQL模式导致默认值0不被允许(常见于timestamp字段设置默认0的情况)。解决方案:修改默认值,或者修改sql_mode。根据引用[1]的例子,我们看到他们使用两个timestamp字段,其中一个默认值为'0000-00-0000:00:00'(这样在严格模式下会报错1067),另一个是`CURRENT_TIMESTAMP`。解决方法是:-将默认值改为一个合法的值,比如将`create_time`的默认值改为`CURRENT_TIMESTAMP`,将`update_time`的默认值设为0(但这样又会产生同样的问题),或者让其中一个字段可以为空(但这样可能不符合业务要求)。-或者,在引用[1]中,他们可能希望`create_time`在插入时自动设置为当前时间,而`update_time`在插入和更新时都自动更新。但在旧版本中,只能有一个字段可以自动设置。所以,在5.6.5之前,只能将`update_time`设置为自动更新,而`create_time`需要手动设置(或者通过触发器)或者设置为允许NULL,然后默认值设为NULL。参考引用[3]的解决方案:修改sql_mode,去掉`NO_ZERO_IN_DATE`和`NO_ZERO_DATE`。但是,这样做可能会导致数据完整性降低。推荐的做法:1.升级MySQL到5.6.5以上版本,因为新版本放宽了这些限制。2.如果必须使用旧版本,那么:-将`create_time`改为`datetime`类型,并设置默认值为`CURRENT_TIMESTAMP`(注意:在旧版本中,datetime不支持,所以这个做法不可行)-或者,将`create_time`改为`timestamp`类型,但不要设置默认值(或默认值为NULL),然后通过代码在插入时设置当前时间。-或者,将`update_time`设置为自动更新(使用`timestamp`类型,并设置`DEFAULTCURRENT_TIMESTAMPONUPDATECURRENT_TIMESTAMP`),而`create_time`则通过触发器在插入时设置当前时间。针对用户原始的错误信息(在问题描述中):1067-Invaliddefaultvalueforupdate_timedatetimeDEFAULTCURRENT_TIMESTAMP我们假设用户试图这样定义:`update_time`datetimeDEFAULTCURRENT_TIMESTAMP在MySQL5.6.5以下版本中,这是不允许的。因此,我们应该建议用户将字段改为`timestamp`类型(前提是接受timestamp的范围限制):`update_time`timestampDEFAULTCURRENT_TIMESTAMP同时,如果用户还想要一个`create_time`字段,并且也希望在插入时设置为当前时间,那么我们可以这样处理:`create_time`datetime,--不设置默认值,但通过应用程序或触发器设置`update_time`timestampDEFAULTCURRENT_TIMESTAMPONUPDATECURRENT_TIMESTAMP或者,如果用户使用的是5.6.5以上版本,可以这样定义:`create_time`datetimeDEFAULTCURRENT_TIMESTAMP,--插入时自动设置`update_time`datetimeDEFAULTCURRENT_TIMESTAMPONUPDATECURRENT_TIMESTAMP--插入和更新时自动更新但是注意:在5.6.5以上版本,允许有多个字段设置自动属性。另外,关于错误1064的原始问题,我们已经在上一次回答中解决了,主要是类型重复定义(int后面又跟datetime)和默认值不匹配的问题。总结用户当前的错误(1067)的解决方案步骤:1.确认MySQL版本:执行`SELECTVERSION();`2.如果版本低于5.6.5,则不能为`datetime`字段设置默认值为`CURRENT_TIMESTAMP`,必须改为`timestamp`类型。3.检查是否违反了一个只能有一个字段设置自动初始化的限制(如果是旧版本,则只能有一个`timestamp`字段可以设置`DEFAULTCURRENT_TIMESTAMP`,而另一个只能通过触发器或应用程序控制)。4.如果版本足够,但仍然出现1067错误,则检查sql_mode。执行`SELECT@@sql_mode;`,查看是否包含`NO_ZERO_DATE`和`NO_ZERO_IN_DATE`。如果包含,并且你的结构中有timestamp字段默认值为0,那么就会出现错误。此时,要么修改结构去掉这些默认值,要么修改sql_mode(不推荐)。在引用[2]中提到,`updated_at`字段报错1067,而`created_at`字段是`datetime`类型默认值为NULL就没有报错。这进一步说明问题可能出在默认值上。所以,针对用户的问题,我们给出如下建示例:假设MySQL版本>=5.6.5:```sqlCREATETABLEyour_table(...,`create_time`datetimeDEFAULTCURRENT_TIMESTAMP,--插入时自动记录创建时间`update_time`datetimeDEFAULTCURRENT_TIMESTAMPONUPDATECURRENT_TIMESTAMP--插入和更新时自动更新);```如果版本低于5.6.5:```sqlCREATETABLEyour_table(...,`create_time`datetimeNOTNULL,--不能设置DEFAULTCURRENT_TIMESTAMP,需要手动或通过触发器设置`update_time`timestampDEFAULTCURRENT_TIMESTAMPONUPDATECURRENT_TIMESTAMP--自动更新);```然后,为了在插入时设置`create_time`,我们可以使用触发器:```sqlDELIMITER$$CREATETRIGGERset_create_timeBEFOREINSERTONyour_tableFOREACHROWBEGINIFNEW.create_timeISNULLTHENSETNEW.create_time=CURRENT_TIMESTAMP;ENDIF;END$$DELIMITER;```另外,也可以考虑将`create_time`改为`timestamp`类型,但是要注意只能有一个自动初始化和更新的字段(在旧版本中),所以如果`update_time`已经使用了自动更新,那么`create_time`就不能设置为`DEFAULTCURRENT_TIMESTAMP`(除非去掉`update_time`的自动初始化)。因此,在旧版本中,我们通常使用触发器来实现两个时间戳。最后,关于引用[3]中修改sql_mode的方法,虽然可以暂时解决问题,但并不是最佳实践,因为这样会允许插入“0000-00-0000:00:00”这样的日期,可能会带来数据一致性问题。因此,我们建议优先通过修改结构或使用触发器来解决问题。现在,我们为用户提供两个可能场景的解决方案。场景一:MySQL版本>=5.6.5解决方案示例:```sqlCREATETABLE`ent_admin_user`(`id`int(11)NOTNULLAUTO_INCREMENT,`username`varchar(255)NOTNULL,`password`varchar(255)NOTNULL,`last_login_ip`varchar(255)DEFAULTNULL,`last_login_time`datetimeDEFAULTNULL,`listorder`int(11)DEFAULT0,--这里改为int类型,假设是排序字段,设置默认值0`status`int(11)DEFAULT0,--同样,改为int类型,默认0`create_time`datetimeDEFAULTCURRENT_TIMESTAMP,--使用datetime类型,默认当前时间`update_time`datetimeDEFAULTCURRENT_TIMESTAMPONUPDATECURRENT_TIMESTAMP,--自动更新PRIMARYKEY(`id`))ENGINE=InnoDBDEFAULTCHARSET=utf8;```场景二:MySQL版本<5.6.5解决方案示例(使用timestamp类型并借助触发器):```sqlCREATETABLE`ent_admin_user`(`id`int(11)NOTNULLAUTO_INCREMENT,`username`varchar(255)NOTNULL,`password`varchar(255)NOTNULL,`last_login_ip`varchar(255)DEFAULTNULL,`last_login_time`datetimeDEFAULTNULL,`listorder`int(11)DEFAULT0,`status`int(11)DEFAULT0,`create_time`datetimeNOTNULL,--在旧版本中,datetime不能设置CURRENT_TIMESTAMP,留空,由触发器填充`update_time`timestampDEFAULTCURRENT_TIMESTAMPONUPDATECURRENT_TIMESTAMP,--使用timestamp自动更新PRIMARYKEY(`id`))ENGINE=InnoDBDEFAULTCHARSET=utf8;--创建触发器,在插入时设置create_timeDELIMITER$$CREATETRIGGERbefore_insert_ent_admin_userBEFOREINSERTON`ent_admin_user`FOREACHROWBEGINSETNEW.create_time=IFNULL(NEW.create_time,NOW());END$$DELIMITER;```注意:在场景二中,当插入一行时,如果没有指定`create_time`的值,触发器会将其设为当前时间。请用户根据实际MySQL版本选择相应的方案。如果用户已经有一个,并且需要修改字段,可以使用ALTERTABLE语句。比如,修改`update_time`字段:```sql--如果原字段为datetime类型且版本>=5.6.5,可以改为:ALTERTABLEent_admin_userMODIFYCOLUMNupdate_timedatetimeDEFAULTCURRENT_TIMESTAMPONUPDATECURRENT_TIMESTAMP;--如果版本低,可以改为timestamp类型:ALTERTABLEent_admin_userMODIFYCOLUMNupdate_timetimestampDEFAULTCURRENT_TIMESTAMPONUPDATECURRENT_TIMESTAMP;```注意:修改结构时,如果已有数据,要注意类型转换是否安全。最后,如果用户遇到任何权限问题或触发器权限问题,需要确保有足够的权限创建触发器。我们提供了详细的解决方案,请用户根据实际情况调整。§§相关问题§§1.如何查看MySQL的版本和当前的sql_mode设置?2.在MySQL中,如何安全地修改sql_mode?3.如何在已有的中添加自动更新时间戳字段?4.使用触发器自动设置时间戳有什么优缺点?</think> ### 错误分析与解决方案:MySQL Error 1067 (Invalid default value for update_time) #### 错误原因分析 1. **版本限制问题** MySQL 5.6.5 之前版本中,`datetime` 字段**不能使用** `CURRENT_TIMESTAMP` 作为默认值: ```sql `update_time` datetime DEFAULT CURRENT_TIMESTAMP -- ✘ 5.6.5 之前版本不支持 ``` 2. **SQL模式冲突** 当 SQL 模式包含 `NO_ZERO_DATE` 和 `NO_ZERO_IN_DATE`(默认启用)时,时间戳字段禁止使用 `0000-00-00 00:00:00` 作为默认值: ```sql `create_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' -- ✘ 违反零日期规则[^1] ``` 3. **字段类型不匹配** `CURRENT_TIMESTAMP` 是时间戳特有的默认值语法,不能用于 `int` 类型字段: ```sql `create_time` int(11) DEFAULT CURRENT_TIMESTAMP -- ✘ 类型与默认值不兼容 ``` --- ### 分版本解决方案 #### 方案一:MySQL ≥ 5.6.5 版本 ```sql CREATE TABLE your_table ( ..., -- 合法定义(支持 datetime 使用 CURRENT_TIMESTAMP) `create_time` datetime DEFAULT CURRENT_TIMESTAMP, `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ); ``` #### 方案二:MySQL < 5.6.5 版本 ```sql CREATE TABLE your_table ( ..., -- 使用 TIMESTAMP 类型替代 datetime `create_time` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, `update_time` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ); ``` #### 临时绕过方案(不推荐) 修改 SQL 模式移除零日期限制: ```sql SET GLOBAL sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'; -- 移除了 NO_ZERO_DATE[^3] ``` > **注意**:此方案会降低数据完整性标准,仅限临时使用。 --- ### 最佳实践建议 1. **统一时间字段类型** 所有时间字段建议统一使用 `TIMESTAMP`(范围:1970-2038)或 `DATETIME`(范围:1000-9999): ```sql -- 标准定义 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ``` 2. **替代零日期方案** 用 `NULL` 或有效日期替代无效零日期: ```sql -- 允许为 NULL 的方案 `create_time` TIMESTAMP NULL DEFAULT NULL -- 设置有效默认日期 `create_time` TIMESTAMP DEFAULT '1970-01-01 00:00:01' ``` 3. **使用触发器实现双时间戳** 当需要同时维护 `create_time` 和 `update_time` 且版本受限时: ```sql CREATE TRIGGER before_insert_your_table BEFORE INSERT ON your_table FOR EACH ROW SET NEW.create_time = NOW(); ``` --- ### 核心原理说明 - **时间戳与日期类型差异**: `TIMESTAMP` 存储 UTC 时间(自动转换时区),`DATETIME` 存储字面时间。 函数支持:$ \text{TIMESTAMP}\leftrightarrow \text{UNIX\_TIMESTAMP()} $ 可相互转换,但 `CURRENT_TIMESTAMP` 返回值依赖于类型。 - **默认值限制方程**: 在严格 SQL 模式下,允许的默认值需满足: $$ \text{default\_value} \notin \{ \text{零日期} \} \cup \{ \text{无效格式} \} $$ 同时满足 $\text{字段类型} \in \{ \text{TIMESTAMP}, \text{DATETIME} \}$ 时才支持 `CURRENT_TIMESTAMP`[^1][^2]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值