解决办法
在 MySQL 5.7 以上版本中,启用了严格模式。
在配置文件中 /etc/mysql/my.cnf 中找到:
sql-model=STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
修改为:
sql-mode=NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
然后重启 MySQL
STRICT_TRANS_TABLES 存储引擎启用严格模式,非法数据值被拒绝
出现此问题的原因
在 MySQL 5.0.2 以前,MySQL 对非法值检查并不严厉,而且为了数据输入还会强制将他们变为合法值。
在 MySQL 5.0.2 以后的版本中,保留了以前的默认行为,但你可以为不良值选择更传统的处理方法,从而使得服务器能够拒绝并放弃出现不良值的语句。
严格模式
如果未使用严格模式,下面的情况是合法的:
- 如果将不正确的值插入到列,如将 null 值插入非 null 列,或将过大的数据插入数值列,MySQL 会将这些列设置为最可能的值,而不是抛出错误信息。
- 如果视图将超过范围的值保存到数值列,MySQL 服务器将保存 0 (最小的可能值) 取而代之,或最大的可能值。
- 对于字符串,MySQL 或保存空字符串,或将字符串可能多的部分保存到列中。
- 如果打算将不是以数值开头的字符串保存到数值列,MySQL 将保存 0 。
- MySQL 允许将特定的不正确日期值保存到 DATE 和 DATETIME 列(如:2000-02-31 或 2000-02-00)。其观点在于,验证日期不是 SQL 服务器的任务。如果 MySQL 能保存日期值并准确检索相同的值,MySQL 就能按给定的值保存它。如果日期完全不正确(超出服务器能保存的范围)将在列中保存特殊的日期值 0000-00-00 取而代之。
- 如果视图将 null 值保存到不接受 null 值的列,对于单行 insert 语句,将出现错误。对于多行 insert 语句或者 insert into ... select 语句,MySQL 服务器会保存针对列数据类型的隐含默认值。一般情况下,对于数值类型,它是 0,对于字符串类型,它是空字符串(''),对于日期和时间类型是 zero 。
- 如果 insert 语句未为列指定值,如果列定义包含明确的 default 子句,MySQL 将插入默认值。如果在定义中没有这类 default 子句,MySQL 会插入列数据类型的隐含默认值。
- 采用前描述规则的原因在于,在语句开始执行前,无法检查这些情况。如果在更新了数行后遇到这类问题,我们不能仅靠回滚解决,这是因为存储引擎可能不支持回滚。中止语句并不是良好的选择,在该情况下,更新完成了 “一半”,这或许是最差的情况。对于本例,较好的方式是 “尽可能做到最好”,然后就像什么都没有发生那样继续执行。
在 MySQL 5.0.2 以后的版本中,可以使用 STRICT_TRANS_TABLES 或 STRICT_ALL_TABLES SQL 模式,选择更严格的处理方式。
STRICT_TRANS_TABLES的工作方式:
- 对于事务性存储引擎,在语句中任何地方出现的不良数据值均会导致放弃语句并执行回滚。
- 对于非事务性存储引擎,如果错误出现在要插入或更新的第 1 行,将放弃语句。(在这种情况下,可以认为语句未改变表,就像事务表一样)。首行后出现的错误不会导致放弃语句。取而代之的是,将调整不良数据值,并给出告警,而不是错误。换句话讲,使用 STRICT_TRANS_TABLES 后,错误值会导致 MySQL 执行回滚操作,如果可以,所有更新到此为止。
要想执行更严格的检查,请启用 STRICT_ALL_TABLES。除了非事务性存储引擎,它与 STRICT_TRANS_TABLES 等同,即使当不良数据出现在首行后的其他行,所产生的错误也会导致放弃语句。这意味着,如果错误出现在非事务性表多行插入或更新过程的中途,仅更新部分结果。前面的行将完成插入或更新,但错误出现点后面的行则不然。对于非事务性表,为了避免这种情况的发生,可使用单行语句,或者在能接受转换警告而不是错误的情况下使用 STRICT_TRANS_TABLES。要想在第 1 场合防止问题的出现,不要使用 MySQL 来检查列的内容。最安全的方式(通常也较快)是,让应用程序负责,仅将有效值传递给数据库。
有了严格的模式选项后,可使用 INSERT IGNORE 或 UPDATE IGNORE 而不是不带 IGNORE 的 INSERT 或 UPDATE,将错误当作告警对待。