全面掌握MySQL 的sql-mode

174 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第8天,点击查看活动详情

MySQL有很多知识点很小,也不起眼,但是往往在工作当中会导致各种各样的问题,其中sql-mode就是其中之一,在不关注的前提下很容易在插入关联数据的时候发生错误,那么今天就全方位的聊聊sql-mode吧。

sql-mode是一组mysql的语法校验规则,定义了mysql应该支持的sql语法、数据校验等规则,不同的MySQL版本默认值也不同,以5.7版本为例,为啥要强调MySQL版本呢?因为MySQL5.6的mode是NO_ENGIN_SUBSTITUTION,表示一个空值,相当于没有模式设置,可以理解为宽松模式。5.7的mode是STRICT_TRANS_TABLES,也就是严格模式。我们通过下面几个方面来认识他。

查看sql-mode

这里要了解的第一个事情就sql-mode的层级,也可以理解为他的作用域,从小到大来说:

会话级别(session):

只对当前操作生效,重启服务器之后就会失效。

这里的会话(Session) 可以理解成通信双方从开始通信到通信结束期间的一个上下文(Context),他和连接还是有区别的,之后我们专门来聊聊。

mysql> select @@session.sql_mode\G;
*************************** 1. row ***************************
@@session.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)
​
ERROR:
No query specified

全局级别(global):

对下次连接开始的操作生效,但是MySQL重启之后会失效。

这里要搞明白一个问题,就是一个连接可以有0到多个会话

mysql> select @@global.sql_mode\G;
*************************** 1. row ***************************
@@global.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)
​
ERROR:
No query specified

配置级别

上述的两种修改都可能在mysql重启之后失效,如果想更加持久生效的化,只能通过配置mysql的配置文件:

系统类型默认配置文件位置
linux(centos)/etc/my.conf
windows安装目录下 my.ini

查看配置当中的sql_mode配置就可以了

sql-mode常用值

ONLY_FULL_GROUP_BY

设置了这个值,如果使用GROUP BY,在SELECT后面出现的字段,在GROUP BY后面必须出现,不然报错如下:

mysql> select * from goods group by origin_place;
ERROR 1055 (42000): Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'practice.goods.id' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by

STRICT_TRANS_TABLES

严格模式控制MySQL如何处理数据更改语句中的无效或缺失值,如INSERT或UPDATE。一个值可能因多种原因无效。例如,它可能具有列的错误数据类型,或者它可能超出了范围。如果要插入的新行不包含定义中没有显式DEFAULT子句的非null列的值,则该值缺失。

比如我们的某个字段设置不能为NULL,而我们插入的数据这个字段为NULL,那么就不能通过,就会报错如下:

create table goods_type(id int primary key auto_increment, t_name varchar(32) not null);
​
mysql> insert into goods_type(t_name) values(null);
ERROR 1048 (23000): Column 't_name' cannot be null
mysql> insert into goods_type(id) values(2);
ERROR 1364 (HY000): Field 't_name' doesn't have a default value

NO_ZERO_IN_DATE

NO_ZERO_IN_DATE模式会影响服务器是否允许年部分不为零但月或日部分为0的日期。 使用这个模式,可以插入 0000-00-00

NO_ZERO_DATE

设置该值,mysql数据库不允许插入零日期,插入零日期会抛出错误而不是警告。

上面的NO_ZERO_IN_DATE可以插入'0000-00-00',如果使用了严格模式STRICT_TRANS_TABLES和NO_ZERO_DATE,那么就不可以插入'0000-00-00'。

ERROR_FOR_DIVISION_BY_ZERO

基于数学严谨性的一个校验模式,对于INSERT或者UPDATE中,如果被除数为0,那么就会产生错误,数据无法插入,MOD(N,M)也是一样,如果不设置mysql会插入或修改为null

NO_AUTO_CREATE_USER

不能使用grant命令创建密码为空的用户。

NO_ENGINE_SUBSTITUTION

如果指定了NO_ENGINE_SUBSTITUTION,我们在创建表或者修改表的时候,如果去指定了不存在或者不支持的存储引擎,那么就会报错,无法创建和修改,如果没有配置NO_ENGINE_SUBSTITUTION,那么就会将我们指定的存储引擎(不支持或者不存在)的存储引擎替换为默认的存储引擎,MySQL5.7后的默认存储引擎为InnoDB,所以就会自动设置为InnoDB。

sql-mode配置

sql-mode的配置需要和查看进行配合,因为需要先知道当前的sql-mode模式,并且也是分为三个层级进行配置的:

设置会话级别的sql-mode

mysql> select @@session.sql_mode\G;
*************************** 1. row ***************************
@@session.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)
​
ERROR:
No query specified
​
mysql> set session 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';
Query OK, 0 rows affected (0.04 sec)
​
mysql>

设置全局级别

这里就只列出命令了,操作流程和结果和会话级别类似

set global 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';

配置级别

首先找到配置文件,上面查看的时候有说,然后进行配置

[mysqld]
sql_mode=ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER

ok,关于sql-mode就先聊到这里,欢迎各位大佬多多指点啊。