【MySQL】MySQL配置中sql_mode的作用

MySQL配置中sql_mode的作用

不知道你有没有踫到过这种问题,在 MySQL8 默认的情况下,我们之前习惯的为 DateTime 类型指定的 0000-00-00 这种格式是无法插入或者修改数据的。其实这种情况就是 MySQL 模式设置的问题,也就是我们今天要讲的 sql_mode 这个参数属性的作用。

sql_mode

根据官网的解释,MySQL 服务器是可以在不同的 SQL 模式中运行的,这个模式会影响 MySQL 支持的 SQL 语法及其执行的数据验证检查。通过模式的设置,可以让不同环境中使用 MySQL 以及其他数据库服务器一起使用 MySQL 变得更加容易。并且,MySQL 可以将这些模式分别运用于不同的客户端,也就是说,它是有 SESSION 会话设置能力的一个系统变量。

如何查看当前系统的 sql_mode 呢?和查看系统变量是一样的。

go 复制代码
mysql> SHOW VARIABLES LIKE 'sql_mode';
+---------------+-----------------------------------------------------------------------------------------------------------------------+
| Variable_name | Value                                                                                                                 |
+---------------+-----------------------------------------------------------------------------------------------------------------------+
| sql_mode      | ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION |
+---------------+-----------------------------------------------------------------------------------------------------------------------+
1 row in set (0.19 sec)

这是我们在虚拟机上通过 RPM 安装的 MySQL ,没有对 sql_mode 进行其它的修改,因此这就是默认的参数。默认情况下,MySQL8 的 sql_mode 就是这些内容。

设置 sql_mode

我们新建一个表,尝试一下日期相关的操作。

go 复制代码
CREATE TABLE `test_mode` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `created_at` datetime NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

接下来,我们插入一条数据。

go 复制代码
mysql> insert into test_mode values(null,'0000-00-00 00:00:00');
ERROR 1292 (22007): Incorrect datetime value: '0000-00-00 00:00:00' for column 'created_at' at row 1

很明显,默认情况下,0000 这种形式插入日期是不行的,这时我们就可以修改 sql_mode ,让它回到 MySQL5 的时代,可以直接插入这种形式的日期数据。

go 复制代码
[server]
sql_mode=ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION

我们可以通过上面的方式直接修改 my.cnf 文件,当然也可以通过 SET sql_mode=xxx 的形式在命令行中动态修改,如果是修改的 my.cnf 文件,则需要重启 MySQL 服务,再次执行插入语句。

go 复制代码
mysql> insert into test_mode values(null,'0000-00-00 00:00:00');
Query OK, 1 row affected (0.03 sec)

mysql> select * from test_mode;
+----+---------------------+
| id | created_at          |
+----+---------------------+
|  1 | 0000-00-00 00:00:00 |
+----+---------------------+
1 row in set (0.00 sec)

如果你之前的数据库是老版本的,现在迁移到 MySQL8 的话,那么去掉 sql_mode 中的 NO_ZERO_IN_DATE 和 NO_ZERO_DATE 参数就可以了,NO_ZERO_DATE 代表是否允许 0000 这种格式,而 NO_ZERO_IN_DATE 则是针对日期和月份部分是否为 00 。

其它的参数其实通过名字我们也能看出来个大概,ERROR_FOR_DIVISION_BY_ZERO 表示除 0 相关的信息,如果插入或更新数据时有除 0 相关操作,比如 MOD(2,0) 这样,就会报出警告。

go 复制代码
mysql> insert into test_mode values(mod(2,0),'0000-00-00 00:00:00');
ERROR 1365 (22012): Division by 0

ONLY_FULL_GROUP_BY 表示拒绝在 SELECT、HAVING 或 GROUP BY 中引用聚合列的查询。看不懂没关系,我们直接看下面的例子。

go 复制代码
-- 有 ONLY_FULL_GROUP_BY
mysql> select sum(id), created_at from test_mode;
ERROR 1140 (42000): In aggregated query without GROUP BY, expression #2 of SELECT list contains nonaggregated column 'ma_test.test_mode.created_at'; this is incompatible with sql_mode=only_full_group_by

-- 删掉 ONLY_FULL_GROUP_BY
mysql> select sum(id), created_at from test_mode;
+---------+---------------------+
| sum(id) | created_at          |
+---------+---------------------+
|       6 | 0000-00-00 00:00:00 |
+---------+---------------------+
1 row in set (0.00 sec)

STRICT_TRANS_TABLES 表示为事务性存储引擎启用严格的SQL模式,如果可能,也为非事务性存储引擎启用严格的SQL模式。这里提到了一个严格模式的概念,严格模式控制 MySQL 如何处理 INSERT 或 UPDATE 等更改语句中的无效或缺失值,比如上面我们说过的日期和除零问题,如果没有 STRICT_TRANS_TABLES 的话,即使有 NO_ZERO_DATE 和 ERROR_FOR_DIVISION_BY_ZERO 参数,也不会出现错误信息。此外,严格模式还会影响到建表和修改表的语句,也就是 CREATE 和 ALTER 语句。

go 复制代码
mysql> set sql_mode='NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> SHOW VARIABLES LIKE 'sql_mode';
+---------------+--------------------------------------------------------------------------------+
| Variable_name | Value                                                                          |
+---------------+--------------------------------------------------------------------------------+
| sql_mode      | NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION |
+---------------+--------------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> insert into test_mode values(mod(2,0) ,'0000-00-00 00:00:00');
Query OK, 1 row affected, 1 warning (0.29 sec)

最后一个 NO_ENGINE_SUBSTITUTION 则是在创建或者修改表时,如果指定了一个不存在的表引擎,是报错还是使用默认引擎替换并警告。

除了上面这些内容之外,sql_mode 还有很多设置,我们再来演示一个 NO_AUTO_VALUE_ON_ZERO 。默认情况下,我们进行数据插入时,给自增长列指定 null 或者 0 ,都会从1开始正常自动增长,但现在我们让 0 不是产生自增长,而是确定的插入一个 0 ,就可以使用这个参数。

go 复制代码
mysql> set sql_mode='xxxxxxx,NO_AUTO_VALUE_ON_ZERO';
Query OK, 0 rows affected (0.00 sec)

mysql> insert into test_mode values(0, null);
Query OK, 1 row affected (0.00 sec)

mysql> insert into test_mode values(0, null);
ERROR 1062 (23000): Duplicate entry '0' for key 'test_mode.PRIMARY'

mysql> select id, created_at from test_mode;
+----+---------------------+
| id | created_at          |
+----+---------------------+
|  0 | NULL                |
|  1 | 0000-00-00 00:00:00 |
|  2 | 0000-00-00 00:00:00 |
|  3 | 0000-00-00 00:00:00 |
|  4 | NULL                |
|  5 | NULL                |
|  6 | 0000-00-00 00:00:00 |
|  7 | 0000-00-00 00:00:00 |
|  8 | 0000-00-00 00:00:00 |
|  9 | NULL                |
| 10 | NULL                |
+----+---------------------+
11 rows in set (0.00 sec)

可以看到在数据中,有了一条 id 为 0 的数据,如果再次插入的话,就会报主键重复,现在 0 就会被当成一个正常的数字 0 ,而不会转化成为 null 的形式进行自动增长操作。

总结

今天的内容简单地介绍了一下 sql_mode 这个属性相关的作用以及一些常用的参数设置。另外还有一部分设置可能使用得比较少,而且大部分情况下我们也不太会去修改这一块的配置,所以大家了解一下即可。在转移或升级到 MySQL8 之后,其实最常见的问题就是上面说过的日期问题,0格式日期这种形式其实是已经过时的方式了,也是不推荐的方式,所以在 MySQL8 中会默认在严格模式下禁用这种形式的日期存储,这一点也是大家需要注意的,能使用正常日期或者使用 null 最好,另外数字时间戳存 0 也是可以表示这类空日期格式的,具体需求还是看你的业务情况来具体分析哦。

参考文档:

https://dev.mysql.com/doc/refman/8.0/en/sql-mode.html

相关推荐
.Eyes1 小时前
OceanBase 分区裁剪(Partition Pruning)原理解读
数据库·oceanbase
苹果醋32 小时前
Java并发编程-Java内存模型(JMM)
java·运维·spring boot·mysql·nginx
MrZhangBaby2 小时前
SQL-leetcode— 2356. 每位教师所教授的科目种类的数量
数据库
一水鉴天2 小时前
整体设计 之定稿 “凝聚式中心点”原型 --整除:智能合约和DBMS的在表层挂接 能/所 依据的深层套接 之2
数据库·人工智能·智能合约
翔云1234562 小时前
Python 中 SQLAlchemy 和 MySQLdb 的关系
数据库·python·mysql
孙霸天3 小时前
Ubuntu20系统上离线安装MongoDB
数据库·mongodb·ubuntu·备份还原
Java 码农3 小时前
nodejs mongodb基础
数据库·mongodb·node.js
TDengine (老段)3 小时前
TDengine IDMP 运维指南(4. 使用 Docker 部署)
运维·数据库·物联网·docker·时序数据库·tdengine·涛思数据
TDengine (老段)3 小时前
TDengine IDMP 最佳实践
大数据·数据库·物联网·ai·时序数据库·tdengine·涛思数据
彬彬醤4 小时前
Mac怎么连接VPS?可以参考这几种方法
大数据·运维·服务器·数据库·线性代数·macos·矩阵