MySQL中sql_mode、隐式类型转换、5.6/5.7的默认配置引发的问题

先介绍下标题中的三个锅

sql_mode

以下是MySQL官网文档原文:

The MySQL server can operate in different SQL modes, and can apply these modes differently for different clients, depending on the value of the sql_mode system variable. DBAs can set the global SQL mode to match site server operating requirements, and each application can set its session SQL mode to its own requirements.

Modes affect the SQL syntax MySQL supports and the data validation checks it performs. This makes it easier to use MySQL in different environments and to use MySQL together with other database servers.

这个模式配置会影响语法检查和数据校验。属于一种偏好设置,用于在各种场合更好地和其他数据库兼容。

隐式类型转换

这个概念和很多程序语言中的类型转换是一个概念,也就是在一些函数和操作符进行运算前,对被操作数的类型是有要求的,如果传入的实际数据类型不匹配,那么系统会自动暗暗的尽力(往往太过尽力,丢失了准度)进行转换以达到类型一致的目的。

sql_mode在不同版本中的默认配置

  • 5.6版本对于sql_mode的默认配置为(摘自官网文档):
The default SQL mode is NO_ENGINE_SUBSTITUTION.
  • 5.7版本对于sql_mode的默认配置为(摘自官网文档):
The default SQL mode in MySQL 5.7 includes these modes: ONLY_FULL_GROUP_BY, STRICT_TRANS_TABLES, NO_ZERO_IN_DATE, NO_ZERO_DATE, ERROR_FOR_DIVISION_BY_ZERO, NO_AUTO_CREATE_USER, and NO_ENGINE_SUBSTITUTION.

这其中主要的一个区别是 STRICT_TRANS_TABLES 模式的启用与否。

STRICT_TRANS_TABLES 在MySQL 5.7 版本中默认启用。

What’s STRICT_TRANS_TABLES

继续摘官网文档吧:

Strict SQL Mode
Strict mode controls how MySQL handles invalid or missing values in data-change statements such as INSERT or UPDATE. A value can be invalid for several reasons. For example, it might have the wrong data type for the column, or it might be out of range. A value is missing when a new row to be inserted does not contain a value for a non-NULL column that has no explicit DEFAULT clause in its definition. (For a NULL column, NULL is inserted if the value is missing.) Strict mode also affects DDL statements such as CREATE TABLE.

If strict mode is not in effect, MySQL inserts adjusted values for invalid or missing values and produces warnings (see Section 13.7.5.41, “SHOW WARNINGS Syntax”). In strict mode, you can produce this behavior by using INSERT IGNORE or UPDATE IGNORE.
For statements such as SELECT that do not change data, invalid values generate a warning in strict mode, not an error.

重点: 启用这个模式,可以在处理insert和update语句上避免一些非法数据引发的错误(事实上, 避免错误的方式就是抛出错误,不再继续执行)。除非用insert ignore 或者 update ignore 盖掉。最后针对select语句,启用了也依然不会那么严格,发现非法数值还是以warning形式报。

开始实验

好,现在进行实验来看看这个strict mode 启用与否能帮你省下多大的麻烦。

表结构(简单起见):

user (
	id int(10) primary key,
	user_id varchar(20),
	phone_num varchar(20)
)

不启用STRICT MODE, 后果惨了

mysql> update user set phone_num='1' where user_id=0;
Query OK, 0 rows affected, 259 warnings (0.04 sec)
Rows matched: 259  Changed: 0  Warnings: 259

启用STRICT MODE, 报ERROR,惨剧避免

mysql> update user set phone_num='1' where user_id=0;
ERROR 1292 (22007): Truncated incorrect DOUBLE value: 'abcdde000002'

这里有一个特殊之处,就是正好拿字符串和整数0进行= 操作符运算,才触发了问题点。= 操作符需要两个操作数类型相同, varchar类型遇上integer, 转varchar为decimal, 字符串内容开头含非数字无法转为有效整数的情况则转出来就是0。于是杯具的where clause就废了。

小结

MySQL 对于类型转换不是那么严格,而且据我所知, 还没有明确的语法或者语句可以强制让MySQL严格处理类型转换。官网文档也没有像SQL Server和Postgres的文档那样明确指明转换优先级。所以如果真的要有效避免这类模糊的语义导致的一些严重后果的话,认真检查所有warnings是必须的。

References:

MySQL 5.7 Documentation Home. 2017.
Chapter 5.1.8 Server SQL Modes
Chapter 12.2 Type Conversion in Expression Evaluation

Baron Schwartz. 2015. MySQL Type Conversion Rules

By Lu Jun

80后男,就职于软件行业。习于F*** GFW。人生48%时间陪同电子设备和互联网,美剧迷,高清视频狂热者,游戏菜鸟,长期谷粉,临时果粉,略知摄影。

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.