`

(转)MySQL 5.7默认SQL模式带来的问题总结

 
阅读更多
http://www.ywnds.com/?p=8865

在使用MySQL 5.7版本时,已经碰到了两起因为SQL_MODE的改变而导致的问题了,究其原因都是因为MySQL 5.7控制的更加严格了,所以在MySQL 5.6或MySQL 5.5有些SQL语句就无法在MySQL 5.7执行了(会给开发带来一个很诡异的问题,就是代码一样、环境一样、SQL一样、测试跟正式怎么就报错呢?)。

说一下我碰到的两起事件:

1、某些GROUP BY的SQL语句无法执行了。

2、创建表时使用日期数据类型指定的默认值为0000-00-00时报错。

先看一下,MySQL 5.6&MySQL 5.7默认的SQL模式。


# MySQL 5.6 Default SQL_MODE;
mysql> select @@sql_mode;
+--------------------------------------------+
| @@sql_mode                                 |
+--------------------------------------------+
| STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION |
+--------------------------------------------+
1 row in set (0.00 sec)

# MySQL 5.7 Default SQL_MODE;
mysql> select @@sql_mode;
+-------------------------------------------------------------------------------------------------------------------------------------------+
| @@sql_mode                                                                                                                                |
+-------------------------------------------------------------------------------------------------------------------------------------------+
| ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION |
+-------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

# MySQL 5.6 Default SQL_MODE;
mysql> select @@sql_mode;
+--------------------------------------------+
| @@sql_mode                                 |
+--------------------------------------------+
| STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION |
+--------------------------------------------+
1 row in set (0.00 sec)

# MySQL 5.7 Default SQL_MODE;
mysql> select @@sql_mode;
+-------------------------------------------------------------------------------------------------------------------------------------------+
| @@sql_mode                                                                                                                                |
+-------------------------------------------------------------------------------------------------------------------------------------------+
| ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION |
+-------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
先简单说一下每个参数的含义:

ONLY_FULL_GROUP_BY
在严格模式下,不要让GROUP BY部分中的查询指向未选择的列,否则报错。

NO_ZERO_DATE
在严格模式,不要将’0000-00-00’做为合法日期。你仍然可以用IGNORE选项插入零日期。在非严格模式,可以接受该日期,但会生成警告。

NO_ZERO_IN_DATE
在严格模式,不接受月或日部分为0的日期,对年不限制。如果使用IGNORE选项,我们为类似的日期插入’0000-00-00’。在非严格模式,可以接受该日期,但会生成警告。

ERROR_FOR_DIVISION_BY_ZERO
在严格模式,在INSERT或UPDATE过程中,如果被零除(或MOD(X,0)),则产生错误(否则为警告)。如果未给出该模式,被零除时MySQL返回NULL。如果用到INSERT IGNORE或UPDATE IGNORE中,MySQL生成被零除警告,但操作结果为NULL。

NO_AUTO_CREATE_USER
在严格模式下,防止GRANT自动创建新用户,除非还指定了密码。

NO_ENGINE_SUBSTITUTION
如果需要的存储引擎被禁用或未编译,可以防止自动替换存储引擎。

STRICT_TRANS_TABLES
为事务存储引擎启用严格模式,也可能为非事务存储引擎启用严格模式,非法数据值被拒绝,下面有详细说明。

严格模式控制MySQL如何处理非法或丢失的输入值。有几种原因可以使一个值为非法。例如,数据类型错误,不适合列,或超出范围。当新插入的行不包含某列的没有显示定义DEFAULT子句的值,则该值被丢失。

对于事务表,当启用STRICT_ALL_TABLES或STRICT_TRANS_TABLES模式时,如果语句中有非法或丢失值,则会出现错误。语句被放弃并滚动。

对于非事务表,如果插入或更新的第1行出现坏值,两种模式的行为相同。语句被放弃,表保持不变。如果语句插入或修改多行,并且坏值出现在第2或后面的行,结果取决于启用了哪个严格选项:

对于STRICT_ALL_TABLES,MySQL返回错误并忽视剩余的行。但是,在这种情况下,前面的行已经被插入或更新。这说明你可以部分更新,这可能不是你想要的。要避免这点,最好使用单行语句,因为这样可以不更改表即可以放弃。

对于STRICT_TRANS_TABLES,MySQL将非法值转换为最接近该列的合法值并插入调整后的值。如果值丢失,MySQL在列中插入隐式 默认值。在任何情况下,MySQL都会生成警告而不是给出错误并继续执行语句。

严格模式不允许非法日期,例如’2004-04-31’。它不允许禁止日期使用“零”部分,例如’2004-04-00’或”零”日期。要想禁止,应在严格模式基础上,启用NO_ZERO_IN_DATE和NO_ZERO_DATE SQL模式。

如果你不使用严格模式(即不启用STRICT_TRANS_TABLES或STRICT_ALL_TABLES模式),对于非法或丢失的值,MySQL将插入调整后的值并给出警告。在严格模式,你可以通过INSERT IGNORE或UPDATE IGNORE来实现。

回答刚开始提出的两个问题?

1、某些GROUP BY的SQL语句无法执行了。

这是因为MySQL 5.7默认加入了ONLY_FULL_GROUP_BY参数。在MySQL的sql_mode是非ONLY_FULL_GROUP_BY语义时。一条select语句,MySQL允许target list中输出的表达式是除聚集函数或group by column以外的表达式,这个表达式的值可能在经过group by操作后变成undefined,例如:


select * from tt group by id;
1
select * from tt group by id;
而对于语义限制都比较严谨的多家数据库,如SQLServer、Oracle、PostgreSql都不支持select target list中出现语义不明确的列,这样的语句在这些数据库中是会被报错的,所以从MySQL 5.7版本开始修正了这个语义,就是我们所说的ONLY_FULL_GROUP_BY语义。而正确的写法如下:


select id from tt group by id;
select id,max(age) from tt group by id;
1
2
select id from tt group by id;
select id,max(age) from tt group by id;
所以ONLY_FULL_GROUP_BY的语义就是确定select target list中的所有列的值都是明确语义,简单的说来,在ONLY_FULL_GROUP_BY模式下,target list中的值要么是来自于聚集函数的结果,要么是来自于group by list中的表达式的值。更详细的请看:MySQL 5.7默认ONLY_FULL_GROUP_BY语义介绍

2、创建表时使用日期数据类型指定的默认值为0000-00-00时报错。

这是因为MySQL 5.7默认加入了NO_ZERO_DATE和NO_ZERO_IN_DATE参数。通过上面的介绍,这两个参数还是有些区别的,NO_ZERO_DATE是完全匹配默认值为0000-00-00时才限制,而NO_ZERO_IN_DATE是在匹配到月或日任意为00时限制(对年不限制)。

当数据类型为date或datetime时,并且使用了NO_ZERO_IN_DATE限制,那么会有如下几种情况:


# 不合法默认值;
CREATE TABLE `test` (`time` datetime NOT NULL DEFAULT '1111-00-01 00:00:00'
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

# 合法默认值;
CREATE TABLE `test` (`time` datetime NOT NULL DEFAULT '1111-01-01 00:00:00'
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

# 合法默认值;
CREATE TABLE `test` (`time` datetime NOT NULL DEFAULT '0000-01-01 00:00:00'
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

# 不合法默认值;
CREATE TABLE `test` (`time` datetime NOT NULL DEFAULT '1111-00-01 00:00:00'
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

# 合法默认值;
CREATE TABLE `test` (`time` datetime NOT NULL DEFAULT '1111-01-01 00:00:00'
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

# 合法默认值;
CREATE TABLE `test` (`time` datetime NOT NULL DEFAULT '0000-01-01 00:00:00'
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
如果使用数据类型为timestamp就有点不太一样了。

timestamp类型取值范围:1970-01-01 00:00:00到2037-12-31 23:59:59。也就是说默认值最少需要调整为1970-01-02 00:00:00。


CREATE TABLE `test` (
  `mid` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `start_time` timestamp NOT NULL DEFAULT '1970-01-02 00:00:00',
  `end_time` timestamp NOT NULL DEFAULT '2037-12-31 23:59:59',
  PRIMARY KEY (`mid`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

CREATE TABLE `test` (
  `mid` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `start_time` timestamp NOT NULL DEFAULT '1970-01-02 00:00:00',
  `end_time` timestamp NOT NULL DEFAULT '2037-12-31 23:59:59',
  PRIMARY KEY (`mid`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
最大值或最小值在timestamp合法范围内才行。网上大概看了看,部分人碰到这个问题都是数据库升级的时候,老的表结构无法在MySQL 5.7应用了。如果想调整SQL模式,直接在配置文件写入sql_mode参数跟上对应的模式参数即可。

3、修改字段类型小于小于最长字段时报错。

当启用严格模式时”STRICT_TRANS_TABLES或STRICT_ALL_TABLES”时,控制MySQL如何处理非法或丢失的输入值。有几种原因可以使一个值为非法。例如,数据类型错误,不适合列,或超出范围。当新插入的行不包含某列的没有显示定义DEFAULT子句的值,则该值被丢失。


mysql> select * from dd;
+--------------------------------+
| log                            |
+--------------------------------+
| 一二三四五六七八九十              |
+--------------------------------+
1 row in set (0.00 sec)

mysql> select * from dd;
+--------------------------------+
| log                            |
+--------------------------------+
| 一二三四五六七八九十              |
+--------------------------------+
1 row in set (0.00 sec)

mysql> set sql_mode='STRICT_TRANS_TABLES';
Query OK, 0 rows affected (0.00 sec)

mysql> alter table dd change column log log varchar(9);        
ERROR 1265 (01000): Data truncated for column 'log' at row 1
                                
mysql> set sql_mode='';                               
Query OK, 0 rows affected (0.00 sec)

mysql> alter table dd change column log log varchar(9);
Query OK, 1 row affected, 1 warning (0.02 sec)    
Records: 1  Duplicates: 0  Warnings: 1

mysql> set sql_mode='STRICT_TRANS_TABLES';
Query OK, 0 rows affected (0.00 sec)

mysql> alter table dd change column log log varchar(9);        
ERROR 1265 (01000): Data truncated for column 'log' at row 1
                                
mysql> set sql_mode='';                               
Query OK, 0 rows affected (0.00 sec)

mysql> alter table dd change column log log varchar(9);
Query OK, 1 row affected, 1 warning (0.02 sec)    
Records: 1  Duplicates: 0  Warnings: 1

MariaDB [dkey]> select * from dd;                              
+-----------------------------+
| log                         |
+-----------------------------+
| 一二三四五六七八九             |
+-----------------------------+
1 row in set (0.00 sec)

MariaDB [dkey]> select * from dd;                              
+-----------------------------+
| log                         |
+-----------------------------+
| 一二三四五六七八九             |
+-----------------------------+
1 row in set (0.00 sec)
可以看到非严格模式,默认把多余数据给截断了。

待续。。。如果再碰到什么问题接着补充。
分享到:
评论

相关推荐

    MySQL5.7中的sql_mode默认值带来的坑及解决方法

    本文主要内容是MySQL升级到5.7版本之后,由于默认的 sql_mode 值带来的坑以及对应的解决方案。 案例一:ONLY_FULL_GROUP_BY 问题描述 MySQL版本从5.6升级至5.7之后,部分SQL执行报错,报错信息如下: ERROR 1055 ...

    redhat/centos版本>=7.x系统一键安装mysql数据库5.7.x并配置基线安全策略shell脚本

    1.脚本将删除所有数据,请提前检查备份,重要!重要!重要!数据物价!!! 2.使用root执行脚本,命令最好是source install-dbsql.sh,否则mysql命令无法...9.可安装目前最新版mysql5.7.32,当有新版本需更改脚本里的包名称

    Linux yum安装指定版本的MySQL(如MySQL5.7)

    mysql 5.7的yum安装方法: 【注意】不要直接yum install mysql ,因为默认yum源安装的maridb,不是mysql 去官网找yum 网址:https://dev.mysql.com/downloads/repo/yum/ 可以直接通过wget下载: wget ...

    mysql 5.7如何安装 mysql 5.7安装配置教程

    这篇文章为大家提供了Mysql的安装包,详细的安装步骤,以及安装过程中出现的问题的解决方案,希望对大家有所帮助…… 工具:Mysql 5.7.12.0安装包 下载 方法/步骤: 点击下载下来的安装工具,进入第一步,选择我接受...

    mysql5.x升级到mysql5.7后导入之前数据库date出错的快速解决方法

    mysql5.x升级至mysql5.7后导入之前数据库date出错的解决方法如下所示:  修改mysql5.7的配置文件即可解决,方法如下:  linux版:找到mysql的安装路径进入默认的为/usr/share/mysql/中,进行对my-default.cnf...

    mysql 5.7安装 MySQL 服务无法启动但是服务没有报告任何错误

    MySQL 5.7的安装有两种方式,一种是下载安装程序,点击安装,另一种是下载压缩包,直接解压,配置一下就可以运行了 个人更喜欢压缩包的形式,所以本文记录的也是压死包形式的安装(配置) ...

    Win10下免安装版MySQL5.7的安装和配置教程详解

    1、MySQL5.7解压 2、新建配置文件my.ini放在D:\Free\mysql-5.7.26-winx64目录下 [mysql] # 设置mysql客户端默认字符集 default-character-set=utf8 [mysqld] #设置3306端口 port = 3306 # 设置mysql的安装目录 ...

    win10 安装 mysql 5.7 msi版的教程图文详解

    mysql 5.7 windows 默认 my.ini 一、下载地址 https://dev.mysql.com/downloads/windows/installer/5.7.html 二、安装 安装没有选目录的地方,自动装到C盘。 1.选择Server only,包括命令行客户端 2.安装VC ++ ...

    MySQL 5.7临时表空间如何玩才能不掉坑里详解

    MySQL 5.7起,开始采用独立的临时表空间(和独立的undo表空间不是一回事哟),命名ibtmp1文件,初始化12M,且默认无上限。 选项 innodb_temp_data_file_path 可配置临时表空间相关参数。 innodb_temp_data_file_...

    Mysql5.7及以上版本 ONLY_FULL_GROUP_BY报错的解决方法

    原因分析:MySQL5.7版本默认设置了 mysql sql_mode = only_full_group_by 属性,导致报错。 其中ONLY_FULL_GROUP_BY就是造成这个错误的罪魁祸首了,对于group by聚合操作,如果在select中的列没有在group by中出现,...

    MySQL 5.1官方简体中文参考手册

    5.7. MySQL访问权限系统 5.7.1. 权限系统的作用 5.7.2. 权限系统工作原理 5.7.3. MySQL提供的权限 5.7.4. 与MySQL服务器连接 5.7.5. 访问控制, 阶段1:连接核实 5.7.6. 访问控制, 阶段2:请求核实 5.7.7. 权限更改...

    mysql5.1中文手册

    LOAD DATA LOCAL安全问题 5.7. MySQL访问权限系统 5.7.1. 权限系统的作用 5.7.2. 权限系统工作原理 5.7.3. MySQL提供的权限 5.7.4. 与MySQL服务器连接 5.7.5. 访问控制, 阶段1:连接核实 ...

    Mysql5.7及8.0版本索引失效情况汇总

    没有特殊说明,测试环境均为MySQL8.0,早期版本可能会有更多情况导致索引失效。8.0失效的情况,早期版本也失效;8.0不失效的情况,早期版本可能失效。 所有测试默认不考虑表为空的情况,特殊情况文中会有说明。 本文...

    Mysql 5.7配置文件详细设置

    [mysqld] # 设置服务端使用的字符集为utf-8 character-set-server=utf8 # 绑定IPv4地址 bind-address = 0.0.0.0 # 设置mysql的端口号 port = 3306 ...# 创建新表时将使用的默认存储引擎 default-storage-eng

    MySQL基础之MySQL 5.7 新增配置

    MySQL是一个多线程的,结构化查询语言(SQL)数据库服务器。SQL在世界上是最流行的数据库语言。...以上内容是小编给大家介绍的mysql5.7 新增配置,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言

    mysql5.7 生成列 generated column用法实例分析

    本文实例讲述了mysql5.7 生成列 generated ...所以 virtual 相较于 stored 需要的的存储空间更少,如果未指定生成列类型,mysql5.7 默认生成列类型为 virtual。 定义生成列的语法: col_name data_type [GENERATED

    MySQL 5.7解压版安装、卸载及乱码问题的图文解决方法

    1. 解压版的安装 (1). 下载压缩包并解压到一个磁盘位置 压缩包下载链接:...#mysql默认端口号 port = 3306 #数据库服务器字符编码 character-set-server = utf8 (3). 添加环境变量 将bin目录添加

    Windows环境下的MYSQL5.7配置文件定位图文分析

    1、MYSQL安装目录 代码如下:select @@basedir; 2、MYSQL数据文件目录 代码如下:select @@datadir; 3、相应地,配置文件在数据文件上一级目录中 4、停止MYSQL服务 代码如下:net stop 本机mysql服务名 5、启动MYSQL...

    解决MySQL 5.7中定位DDL被阻塞的问题

    在MySQL 5.7中,针对MDL,引入了一张新表performance_schema.metadata_locks,该表可对外展示MDL的相关信息,包括其作用对象,类型及持有等待情况。 开启MDL的instrument 但是相关instrument并没有开启(MySQL 8.0是...

Global site tag (gtag.js) - Google Analytics