百度360必应搜狗淘宝本站头条
当前位置:网站首页 > IT技术 > 正文

一文弄懂 MySQL 中的 SQL_MODE

wptr33 2025-02-11 13:45 29 浏览

QL_MODE 是 MySQL 数据库中的一个系统变量,用于控制 MySQL 如何处理 SQL 语句和数据校验。它可以看作是一组约束和规范,确保数据的准确性、完整性和一致性。例如,可以控制如何处理无效日期、是否允许插入不完整的记录、是否区分大小写等。

作者:许祥,爱可生 MySQL DBA 团队成员,负责处理客户 MySQL 及我司自研 DMP 平台日常运维中的问题。

爱可生开源社区出品,原创内容未经授权不得随意使用,转载请联系小编并注明来源。

本文约 2600 字,预计阅读需要 8 分钟。

什么是 SQL_MODE?

SQL_MODE 是 MySQL 数据库中的一个系统变量,用于控制 MySQL 如何处理 SQL 语句和数据校验。它可以看作是一组约束和规范,确保数据的准确性、完整性和一致性。例如,可以控制如何处理无效日期、是否允许插入不完整的记录、是否区分大小写等。

在 MySQL 中,通过设置 SQL_MODE 可以解决下面几类问题:

  1. 完成不同严格程度的数据校验,有效地保障数据准确性
  2. 保证大多数 SQL 符合标准的 SQL 语法,这样应用在不同数据库之间进行迁移时,则不需要对业务 SQL 进行较大的修改。
  3. 在不同数据库之间进行数据迁移之前,通过设置 SQL_MODE 可以使 MySQL 上的数据更方便地迁移到目标数据库中

通过阅读本文,你将收获以下知识点:

  1. MySQL 不同版本的默认 SQL_MODE 值
  2. 常用的 SQL_MODE 设置及其含义
  3. SQL_MODE 在数据迁移中的关键作用

SQL_MODE 有哪些值?

MySQL 5.7 的默认值和描述

  • ONLY_FULL_GROUP_BY:在 GROUP BY 子句中没有出现的列,若出现在 SELECT 列表、HAVING 条件、ORDER BY 条件中时会被拒绝。
  • STRICT_TRANS_TABLES:非法日期,超过字段长度的值插入时,直接报错,拒绝执行。例如,如果向一个整数列插入超出范围的值,将触发错误。
  • NO_ZERO_DATE:针对日期 '0000-00-00',执行逻辑如下:
  • disable:可以正常插入,没有警告。
  • enable:可以正常插入,有警告。
  • 如果 SQL_MODE 中包含 STRICT TRANS TABLES,则日期被拒绝写入,但可以通过加 IGNORE 关键字写入 '0000-00-00',有警告。
  • NO_ZERO_IN_DATE:日期中针对月份和日期部分,如果为 0,比如 '2024-00-00',有不同的执行逻辑:
  • disable:可以正常插入,实际插入值还是 '2024-00-00' 没有警告。
  • enable:可以正常插入,有警告。
  • 如果 SQL_MODE 中包含 STRICT TRANS TABLES 则日期被拒绝写入,但可以通过加 IGNORE 关键字写入 '0000-00-00'。
  • ERROR_FOR_DIVISION_BY_ZERO:除数为 0(包括 MOD (N,0)),执行逻辑如下:
  • disable:插入 NULL,没有警告。
  • enable:插入 NULL,有警告。
  • 如果 SQL_MODE 中包含 STRICTTRANSTABLES,则数据被拒绝写入,但可以通过加 IGNORE 关键字写入 NULL,有警告。
  • NO_AUTO_CREATE_USER:防止使用不带密码子句的 GRANT 语句来创建一个用户。
  • NO_ENGINE_SUBSTITUTION:执行 CREATE TABLE 或者 ALTER TABLE 语句时,如果指定的存储引擎不可用,MySQL 会抛出错误,而不是使用默认存储引擎。
  • disable:CREATE TABLE 会自动替换后执行,ALTER TABLE 不会执行,两个命令都有警告。
  • enable:两个命令直接报错。

MySQL 8.0 的默认值和描述

  • STRICT_TRANS_TABLES:同 MySQL 5.7,确保插入或更新的数据严格符合表的定义。
  • NO_ZERO_DATE:同 MySQL 5.7,禁止日期字段为 '0000-00-00',要求输入有效的日期。
  • NO_ZERO_IN_DATE:同 MySQL 5.7,禁止日期或日期时间字段中的月份或日部分为零。
  • ERROR_FOR_DIVISION_BY_ZERO:同 MySQL 5.7,当除以零时,MySQL 将抛出错误,而不是返回 NULL。
  • NO_AUTO_CREATE_USER:同 MySQL 5.7,禁止通过 GRANT 语句自动创建用户。
  • NO_ENGINE_SUBSTITUTION:同 MySQL 5.7,确保指定的存储引擎必须存在,不会自动替换。

查询 SQL_MODE

-- MySQL 5.7
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_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION |
+---------------+-------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

-- MySQL 8.0
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.00 sec)

SQL_MODE 常见设置模式

SQL_MODE 的常见设置模式是一组可以选择的选项,这些选项可以根据需要组合使用,以改变数据库的行为。以下是一些常见的模式及其含义:

  • ANSI 模式:宽松模式,对插入数据进行校验,如果不符合定义类型或长度,对数据类型调整或截断保存,报 WARNING 警告。
  • STRICT_TRANS_TABLES 模式:严格模式,进行数据的严格校验,错误数据不能插入,报 ERROR 错误。只对支持事务的表有效。
  • STRICT_ALL_TABLES 模式:严格模式,进行数据的严格校验,错误数据不能插入,报 ERROR 错误。对所有表都有效。
  • TRADITIONAL 模式:严格模式,当向 MySQL 数据库插入数据时,进行数据的严格校验,保证错误数据不能插入,报 ERROR 错误。用于事务时,会进行事务的回滚。

在下面的例子中,观察一下非法日期 “2007-04-31”(因为 4 月没有 31 日)在不同 SQL_MODE 下能否正确插入。


mysql> select @@session.sql_mode;
+-------------------------------------------------------------------------------------------------------------------------------------------+
| @@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)

mysql> set session sql_mode='ANSI';
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql> select @@session.sql_mode;
+--------------------------------------------------------------------------------+
| @@session.sql_mode                                                             |
+--------------------------------------------------------------------------------+
| REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ONLY_FULL_GROUP_BY,ANSI |
+--------------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql>  create table t_sql_mode_ansi(d datetime);
Query OK, 0 rows affected (0.01 sec)

mysql> insert into t_sql_mode_ansi values('2007-04-31');
Query OK, 1 row affected, 1 warning (0.00 sec)

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

mysql> set session sql_mode='TRADITIONAL';
Query OK, 0 rows affected, 1 warning (0.00 sec)

 mysql> select @@session.sql_mode;
+------------------------------------------------------------------------------------------------------------------------------------------------------+
| @@session.sql_mode                                                                                                                                   |
+------------------------------------------------------------------------------------------------------------------------------------------------------+
| STRICT_TRANS_TABLES,STRICT_ALL_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,TRADITIONAL,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION |
+------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> insert into t_sql_mode_ansi values('2007-04-31');
ERROR 1292 (22007): Incorrect datetime value: '2007-04-31' for column 'd' at row 1

很显然,在 ANSI 模式下,非法日期可以插人,但是插入值却变为 “0000-00-00 00:00:00” 并且系统给出了 WARNING;而在 TRADITIONAL 模式下,会直接提示日期非法,拒绝插入。STRICT_TRANS_TABLES 模式和 STRICT_ALL_TABLES 模式同样如此。

SQL_MODE 在迁移中的用法

当从 MySQL 迁移到其他异构数据库时,SQL_MODE 的配置和调整在数据迁移过程中起着关键作用。 由于不同数据库系统在 SQL 标准、数据类型处理、错误处理等方面存在差异,正确使用 SQL_MODE 可以帮助确保迁移过程中数据的完整性和一致性。

SQL_MODE 是 MySQL 的一个配置选项,控制着 SQL 语句的解析和执行方式。

例如,SQL_MODE 可以影响 NULL 值的处理、日期格式的验证、GROUP BY 的严格性等。当迁移数据到其他数据库时,目标数据库可能不支持或以不同方式处理这些规则,因此在迁移前需要仔细配置和测试 SQL_MODE。

1. SQL_MODE 在迁移前的调整

在将 MySQL 数据库迁移到其他数据库之前,可以考虑以下步骤来调整 SQL_MODE,以减少迁移时可能出现的兼容性问题:

  • 禁用严格模式:在迁移前,禁用 MySQL 中的严格模式(如 STRICT_TRANS_TABLES)可以帮助识别哪些数据在当前配置下可能会在目标数据库中引发问题。通过禁用严格模式,可以提前发现并处理不兼容的数据。
SET GLOBAL SQL_MODE ='';
  • 禁用 ONLY_FULL_GROUP_BY:MySQL 的 ONLY_FULL_GROUP_BY 模式要求所有非聚合列必须在 GROUP BY 子句中,这在其他数据库中可能不是必需的。禁用该模式可以确保 SQL 查询在目标数据库中能正确执行。
  • 启用宽松模式:通过设置较为宽松的 SQL_MODE(如禁用 NO_ZERO_DATE、NO_ZERO_IN_DATE),可以让 MySQL 接受一些可能在目标数据库中允许的数据格式,并确保这些数据能被顺利迁移。

2. 在迁移过程中处理 SQL_MODE 相关问题

  • 日期和时间处理:一些数据库系统对日期和时间有更严格的要求。例如,0000-00-00 这样的日期在 MySQL 中可能是合法的,但在其他数据库中会引发错误。在迁移前,需要确保数据格式符合目标数据库的要求,或者通过 SQL 脚本清理这些数据。
  • 空字符串与 NULL 的处理:在 MySQL 中,空字符串和 NULL 可能在一些情况下被视为相等,而在其他数据库中并非如此。迁移前,应该明确这些字段的逻辑,并在必要时进行转换。
  • 标识符大小写:MySQL 对标识符的大小写敏感性可能与目标数据库不同。在迁移前,使用 SQL_MODE 的 ANSI_QUOTES 选项可以确保标识符的引用方式符合 SQL 标准,并减少在目标数据库中的兼容性问题。

3. 迁移后的兼容性测试

完成数据迁移后,需要在目标数据库中进行全面的兼容性测试,以确保迁移后的数据和应用程序能正常运行。重点测试的领域包括:

  • 查询结果的准确性:检查涉及 GROUP BY、聚合函数、日期处理等 SQL 查询是否在目标数据库中返回预期结果
  • 数据完整性:确保迁移后的数据没有丢失、截断或被错误转换。
  • 性能:有些 SQL_MODE 设置可能影响查询性能,迁移后需要在目标数据库中优化相关查询。

4. 小结

在从 MySQL 迁移到其他数据库时,合理调整 SQL_MODE 可以显著减少迁移过程中的兼容性问题。通过禁用严格模式、调整日期和空值处理、确保标识符的一致性,以及进行全面的测试,可以确保迁移后的数据和应用程序在新的数据库环境中稳定运行。

5. 迁移过程中需要注意

在数据迁移过程中,可以设置 SQL_MODE 为 NO_TABLE_OPTIONS 模式。这样将去掉 SHOW CREATE TABLE 中的 ENGINE 关键字,获得通用的建表脚本。

测试示例如下:

mysql> show create table test\G
*************************** 1. row ***************************
       Table: test
Create Table: CREATE TABLE `test` (
  `ename` varchar(10) COLLATE utf8mb4_bin DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `sal` decimal(10,2) DEFAULT NULL,
  `hiredate` date DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin
1 row in set (0.00 sec)
mysql>
mysql> set session sql_mode='NO_TABLE_OPTIONS';
Query OK, 0 rows affected, 2 warnings (0.00 sec)

mysql>
mysql> show create table test\G
*************************** 1. row ***************************
       Table: test
Create Table: CREATE TABLE `test` (
  `ename` varchar(10) COLLATE utf8mb4_bin DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `sal` decimal(10,2) DEFAULT NULL,
  `hiredate` date DEFAULT NULL
)
1 row in set (0.00 sec)

总结

  1. SQL_MODE 的 “严格模式” 为 MySQL 提供了很好的数据校验功能,保证了数据的准确性,TRADITIONALSTRICT_TRANS_TABLES 是常用的两种严格模式,要注意两者的区别。
  2. SQL_MODE 的多种模式可以灵活组合,组合后的模式可以更好地满足应用程序的需求。尤其在数据迁移中,SQL_MODE 的使用更为重要。

更多技术文章,请访问:https://opensource.actionsky.com/

相关推荐

MySQL进阶五之自动读写分离mysql-proxy

自动读写分离目前,大量现网用户的业务场景中存在读多写少、业务负载无法预测等情况,在有大量读请求的应用场景下,单个实例可能无法承受读取压力,甚至会对业务产生影响。为了实现读取能力的弹性扩展,分担数据库压...

Postgres vs MySQL_vs2022连接mysql数据库

...

3分钟短文 | Laravel SQL筛选两个日期之间的记录,怎么写?

引言今天说一个细分的需求,在模型中,或者使用laravel提供的EloquentORM功能,构造查询语句时,返回位于两个指定的日期之间的条目。应该怎么写?本文通过几个例子,为大家梳理一下。学习时...

一文由浅入深带你完全掌握MySQL的锁机制原理与应用

本文将跟大家聊聊InnoDB的锁。本文比较长,包括一条SQL是如何加锁的,一些加锁规则、如何分析和解决死锁问题等内容,建议耐心读完,肯定对大家有帮助的。为什么需要加锁呢?...

验证Mysql中联合索引的最左匹配原则

后端面试中一定是必问mysql的,在以往的面试中好几个面试官都反馈我Mysql基础不行,今天来着重复习一下自己的弱点知识。在Mysql调优中索引优化又是非常重要的方法,不管公司的大小只要后端项目中用到...

MySQL索引解析(联合索引/最左前缀/覆盖索引/索引下推)

目录1.索引基础...

你会看 MySQL 的执行计划(EXPLAIN)吗?

SQL执行太慢怎么办?我们通常会使用EXPLAIN命令来查看SQL的执行计划,然后根据执行计划找出问题所在并进行优化。用法简介...

MySQL 从入门到精通(四)之索引结构

索引概述索引(index),是帮助MySQL高效获取数据的数据结构(有序),在数据之外,数据库系统还维护者满足特定查询算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构...

mysql总结——面试中最常问到的知识点

mysql作为开源数据库中的榜一大哥,一直是面试官们考察的重中之重。今天,我们来总结一下mysql的知识点,供大家复习参照,看完这些知识点,再加上一些边角细节,基本上能够应付大多mysql相关面试了(...

mysql总结——面试中最常问到的知识点(2)

首先我们回顾一下上篇内容,主要复习了索引,事务,锁,以及SQL优化的工具。本篇文章接着写后面的内容。性能优化索引优化,SQL中索引的相关优化主要有以下几个方面:最好是全匹配。如果是联合索引的话,遵循最...

MySQL基础全知全解!超详细无废话!轻松上手~

本期内容提醒:全篇2300+字,篇幅较长,可搭配饭菜一同“食”用,全篇无废话(除了这句),干货满满,可收藏供后期反复观看。注:MySQL中语法不区分大小写,本篇中...

深入剖析 MySQL 中的锁机制原理_mysql 锁详解

在互联网软件开发领域,MySQL作为一款广泛应用的关系型数据库管理系统,其锁机制在保障数据一致性和实现并发控制方面扮演着举足轻重的角色。对于互联网软件开发人员而言,深入理解MySQL的锁机制原理...

Java 与 MySQL 性能优化:MySQL分区表设计与性能优化全解析

引言在数据库管理领域,随着数据量的不断增长,如何高效地管理和操作数据成为了一个关键问题。MySQL分区表作为一种有效的数据管理技术,能够将大型表划分为多个更小、更易管理的分区,从而提升数据库的性能和可...

MySQL基础篇:DQL数据查询操作_mysql 查

一、基础查询DQL基础查询语法SELECT字段列表FROM表名列表WHERE条件列表GROUPBY分组字段列表HAVING分组后条件列表ORDERBY排序字段列表LIMIT...

MySql:索引的基本使用_mysql索引的使用和原理

一、索引基础概念1.什么是索引?索引是数据库表的特殊数据结构(通常是B+树),用于...