深入理解 MySQL 中的 SQL_MODE
SQL_MODE 是 MySQL 数据库中的一个系统变量,用于控制 MySQL 如何处理 SQL 语句和数据校验。它可以看作是一组约束和规范,确保数据的准确性、完整性和一致性。例如,可以控制如何处理无效日期、是否允许插入不完整的记录、是否区分大小写等。
> 作者:许祥,爱可生 MySQL DBA 团队成员,负责处理客户 MySQL 及我司自研 DMP 平台日常运维中的问题。 > >爱可生开源社区出品,原创内容未经授权不得随意使用,转载请联系小编并注明来源。 > >本文约 2600 字,预计阅读需要 8 分钟。
什么是 SQL_MODE?
SQL_MODE 是 MySQL 数据库中的一个系统变量,用于控制 MySQL 如何处理 SQL 语句和数据校验。它可以看作是一组约束和规范,确保数据的准确性、完整性和一致性。例如,可以控制如何处理无效日期、是否允许插入不完整的记录、是否区分大小写等。
在 MySQL 中,通过设置 SQL_MODE 可以解决下面几类问题:
- 完成不同严格程度的数据校验,有效地保障数据准确性。
- 保证大多数 SQL 符合标准的 SQL 语法,这样应用在不同数据库之间进行迁移时,则不需要对业务 SQL 进行较大的修改。
- 在不同数据库之间进行数据迁移之前,通过设置 SQL_MODE 可以使 MySQL 上的数据更方便地迁移到目标数据库中。
通过阅读本文,你将收获以下知识点:
- MySQL 不同版本的默认 SQL_MODE 值
- 常用的 SQL_MODE 设置及其含义
- 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)
总结
- SQL_MODE 的“严格模式”为 MySQL 提供了很好的数据校验功能,保证了数据的准确性,TRADITIONAL 和 STRICT_TRANS_TABLES 是常用的两种严格模式,要注意两者的区别。
- SQL_MODE 的多种模式可以灵活组合,组合后的模式可以更好地满足应用程序的需求。尤其在数据迁移中,SQL_MODE 的使用更为重要。
更多技术文章,请访问:https://opensource.actionsky.com/
关于 SQLE
SQLE 是一款全方位的 SQL 质量管理平台,覆盖开发至生产环境的 SQL 审核和管理。支持主流的开源、商业、国产数据库,为开发和运维提供流程自动化能力,提升上线效率,提高数据质量。
✨ Github:https://github.com/actiontech/sqle
📚 文档:https://actiontech.github.io/sqle-docs/
💻 官网:https://opensource.actionsky.com/sqle/
👥 微信群:请添加小助手加入 ActionOpenSource

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
基于 TDMQ for Apache Pulsar 的跨地域复制实践
导语 自2024年9月6日起,TDMQ Pulsar 版专业集群支持消息、元数据两级跨地域复制功能,消息级复制解决用户全球地域的数据统一归档问题,元数据级复制提供解决用户核心业务跨地域容灾的场景。 用户在跨地域场景遇到的疑问和挑战 在跨地域相关场景下,通常有两大类原始需求: 跨地域容灾 TDMQ Pulsar 版目前支持在同一地域下进行跨可用区部署,这种部署方式能够在一定程度上提高系统的可用性和容灾能力。 如上图所示,在广州地域内的3个可用区部署了 TDMQ Pulsar 版集群。如果广州的某个可用区无法使用,系统仍然可以依靠其他两个可用区继续提供服务。 然而,金融级客户通常需要更高的容灾标准,希望能够通过跨地域部署来实现跨地域的容灾,以确保在极端情况下,比如发生自然灾害或网络故障时,也能保证业务的连续性和数据的安全性。 如上图展示的架构,用户业务可以在主地域进行日常的生产和消费,同时在其他地域进行数据备份和灾备。这种架构能够实现当一个数据中心发生故障时,迅速切换到另一个地域的数据中心,确保业务不中断,以保护数据的完整性和可用性。 全球数据归档 这类需求一般来源于国际站用户,例如某客户...
- 下一篇
数据资产入表全流程解析,助力企业数据要素价值释放
数据资产入表即数据资产会计核算,指的是把有价值的数据编制进资产负债表,作为企业沉淀的无形资产,让数据要素的交易流通变得合规,数据价值可计算。 2023年8月21日,财政部发布《企业数据资源相关会计处理暂行规定》,并于2024年1月1日开始实施,首次将数据资源纳入企业会计核算体系,明确了数据资产入表的标准和要求,标志着数据资产在会计领域的正式确认,并开启了数据要素产业化的新时代。随后国资委、中国资产评估协会、中国银行业协会等多个组织资产入表探索尝试。 对数据资产入表的推动,一方面有利于帮助企业建立更加完善的数据资产管理体系,助力数据驱动型企业吸引外部融资、优化财务结构、提升公司价值;另一方面能够促进不同企业机构之间的数据共享与合作,建立更加开放的数据生态系统,提升社会服务的质量和效率,优化资源配置,推动经济结构升级。 数据资产入表流程通常包括数据资源化、资源产品化和产品资产化三个步骤。实践中,经过这三个步骤后形成可清晰辨认、应用场景明确、价值可以计量的数据资产凭证,并在满足资产的确认条件后形成数据资产入表。本文将以“五步法”为基础,为大家介绍从企业角度具体如何实施数据资产入表。 想要实现...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- CentOS8编译安装MySQL8.0.19
- CentOS6,CentOS7官方镜像安装Oracle11G
- CentOS7,8上快速安装Gitea,搭建Git服务器
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- MySQL8.0.19开启GTID主从同步CentOS8
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- Red5直播服务器,属于Java语言的直播服务器
- CentOS6,7,8上安装Nginx,支持https2.0的开启