MySQL Server SQL Modes
MySQL 服务器可以在不同的 SQL modes 下运行,并且可以根据 sql_mode 系统变量的值将这些模式应用于不同的客户端。
SQL modes 会影响 MySQL 支持的 SQL 语法以及它执行的数据验证检查。
8.0 版本的默认 6 个 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
一、设置
SQL 模式是在启动阶段配置的,通过命令行或配置文件。
1. 语法
sql-mode="modes" # 设置
sql-mode="" # 清除
2. 动态设置
SET GLOBAL sql_mode = 'modes';
SET SESSION sql_mode = 'modes';
3. 查看
SELECT @@GLOBAL.sql_mode;
SELECT @@SESSION.sql_mode;
4. 注意事项
- 一旦使用自定义分区建表,之后就不要再更改 SQL 模式,否则可能会导致此类表的行为发生重大变化,并可能导致数据丢失或损坏
- 主从复制中,主从的 SQL 模式应该一致,否则在复制分区表时也会导致问题
二、完整模式列表
如果没有特殊需求,使用默认的 SQL mode 就可以了。
1. 日期和时间
(1)ALLOW_INVALID_DATES
- 不对日期进行全面检查
- 仅检查月份是否是 1-12 和日期是否是 1-31
- 此模式适用于 DATE 和 DATETIME 列,不适用于始终需要有效日期的 TIMESTAMP 列
(2)TIME_TRUNCATE_FRACTIONAL
8.0 版本新增 mode。
- 当将一个具有小数位的秒值插入 TIME/DATE/TIMESTAMP 类型但小数位数较少的列时,默认会进行四舍五入。如果启动此模式,则会截断。
2. 特殊字符
(1)ANSI_QUOTES
- 将双引号
"视为标识符(如反引号 `),而不作为字符串引号字符 - 不能使用双引号将字符串引起来,因为双引号已经被解释为标识符了
(2)NO_BACKSLASH_ESCAPES
- 禁止在字符串和标识符中将反斜杠字符(\)用作转义字符
(3)IGNORE_SPACE
-
允许函数名称和括号
(之间的空格,这会将内置的函数名称作为保留字mysql> CREATE TABLE count (i INT); ERROR 1064 (42000): You have an error in your SQL syntax -- 报错原因:表名和内置函数 count() 名称冲突 -- 解决方法:使用反引号 mysql> CREATE TABLE `count` (i INT); -
此模式适用于内置函数,不适用于用户定义的函数或存储的函数
(4)PIPES_AS_CONCAT
- 将管道符
||作为字符串连接运算符,类似 concat(),而不是 or 的同义词
3. 数据类型
(1)NO_AUTO_VALUE_ON_ZERO
- 自增列插入 0 不自动生成序列号,作用于自增列
通常,可以在自增列中插入 NULL 或 0 来生成下一个序列号。此模式下,抑制 0,只有 NULL 才会生成下一个序列号。 - 不建议自增列存储 0,例如 mysqldump 导出数据然后重新导入时,自增列遇到 0 会重新生成序列号,导致和原表内容不一致
- mysqldump 时,会自动开启此模式
(2)REAL_AS_FLOAT
- 将 REAL 视为 FLOAT 的同义词
默认情况下,MySQL 将 REAL 视为 DOUBLE 的同义词
4. 运算比较
(1)HIGH_NOT_PRECEDENCE
-
NOT 运算的优先级。启用此模式,会使用旧版的 NOT 运算优先级
默认情况下,NOT a BETWEEN b AND c被解析为NOT (a BETWEEN b AND c),旧版会解析成(NOT a) BETWEEN b AND c。 -
举例
mysql> SET sql_mode = ''; mysql> SELECT NOT 1 BETWEEN -5 AND 5; -0 mysql> SET sql_mode = 'HIGH_NOT_PRECEDENCE'; mysql> SELECT NOT 1 BETWEEN -5 AND 5; -1
(2)NO_UNSIGNED_SUBTRACTION
-
默认情况下,整数之间的减法(如果其中一个类型为 unsigned)将产生 unsigned 类型的结果。如果结果为负数,还会报错
例如 mysql> SET sql_mode = ''; Query OK, 0 rows affected (0.00 sec) mysql> SELECT CAST(0 AS UNSIGNED) - 1; ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '(cast(0 as unsigned) - 1)' -- 如果开启此模式,上面的结果就是 -1 mysql> SET sql_mode = 'NO_UNSIGNED_SUBTRACTION'; mysql> SELECT CAST(0 AS UNSIGNED) - 1; +-------------------------+ | CAST(0 AS UNSIGNED) - 1 | +-------------------------+ | -1 | +-------------------------+ -
如果将此类操作的结果用于更新 UNSIGNED 整数列,则将结果裁剪为该列类型的最大值,如果启用了此模式,则裁剪为 0
- 启用严格 SQL 模式后,将发生错误,并且列将保持不变
-
启用此模式时,即使任何操作数都是 unsigned 的,减法结果也是 signed
mysql> SET sql_mode=''; mysql> CREATE TABLE test (c1 BIGINT UNSIGNED NOT NULL); mysql> CREATE TABLE t1 SELECT c1 - 1 AS c2 FROM test; mysql> DESCRIBE t1; +-------+---------------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+---------------------+------+-----+---------+-------+ | c2 | bigint(21) unsigned | NO | | 0 | | +-------+---------------------+------+-----+---------+-------+ mysql> SET sql_mode='NO_UNSIGNED_SUBTRACTION'; mysql> CREATE TABLE t2 SELECT c1 - 1 AS c2 FROM test; mysql> DESCRIBE t2; +-------+------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+------------+------+-----+---------+-------+ | c2 | bigint(21) | NO | | 0 | | +-------+------------+------+-----+---------+-------+
5. 表定义
(1)NO_DIR_IN_CREATE
- 创建表时,忽略所有的索引目录和数据目录
此模式在主从复制的从节点上很有用
(2)NO_ENGINE_SUBSTITUTION
- 禁止自动替换引擎,默认已开启
当 create table 或 alter table 时,指定了一个禁用的或未编译的存储引擎,此模式控制是否自动进行替换。
当此模式禁用时,create table 如果指定了不存在的引擎,会使用默认引擎替换,并发出警告;alter table 会发出警告,不做任何操作。当此模式开启时,会报错。
6. 聚合函数
(1)ONLY_FULL_GROUP_BY
- 拒绝查询选择列表、HAVING 条件或 ORDER BY 列表引用的非聚集列既未在 GROUP BY 子句中命名,也未在功能上依赖于 GROUP BY 列(由其唯一确定)。默认已开启
- MySQL 对标准 SQL 的扩展允许在 HAVING 子句中引用选择列表中的别名表达式
- 概括一下就是,HAVING 子句是否允许使用别名
7. 严格模式
(1)STRICT_ALL_TABLES
- 所有引擎开启严格模式,拒绝非法数值
- 包括
这 3 个模式已过时,将来可能会和严格模式合并。- ERROR_FOR_DIVISION_BY_ZERO
- NO_ZERO_DATE
- NO_ZERO_IN_DATE
(2)STRICT_TRANS_TABLES
- 为事务性存储引擎(也有可能是非事务性存储引擎)开启严格模式
如果不能按照给定值插入事务表中,会中止该语句。属于默认模式。 - 包括
这 3 个模式已过时,将来可能会和严格模式合并。- ERROR_FOR_DIVISION_BY_ZERO
- NO_ZERO_DATE
- NO_ZERO_IN_DATE
8. 已过时的模式
(1)ERROR_FOR_DIVISION_BY_ZERO
已过时,应该和严格模式一起使用,否则会警告,默认已开启。
- 除 0 错误,包括 MOD(N,0)
必须与严格模式一起使用,否则发出警告,默认是开启的。 - 操作影响
- 数据变更操作(insert/update)
- 如果禁用此模式,除 0 会插入 NULL,且不警告
- 如果启用此模式,除 0 插入 NULL,且发出警告
- 如果启用此模式和严格模式,除 0 会报错,除非指定 ignore,如 INSERT IGNORE 和 UPDATE IGNORE,这时会插入 NULL,且发出警告
- select 操作
- 除 0 会返回 NULL,无论是否开启严格模式
- 数据变更操作(insert/update)
(2)NO_ZERO_DATE
已过时,应该和严格模式一起使用,否则会警告,默认已开启。
- 禁止 ‘0000-00-00’ 的时间格式
此模式允许 ‘0000-00-00’ 格式,如果禁用此模式,insert 过程不产生警告,如果开启此模式,insert 过程产生警告。
如果开启此模式和严格模式,则不允许 ‘0000-00-00’ 格式,insert 过程报错。除非使用 ignore,如INSERT IGNORE、UPDATE IGNORE。
(3)NO_ZERO_IN_DATE
已过时,应该和严格模式一起使用,否则会警告,默认已开启。
- 是否允许日期中,年份部分非 0,月份部分为 0,影响 ‘2010-00-01’ 或 ‘2010-01-00’,不影响 ‘0000-00-00’
如果禁用此模式,则允许 0,insert 过程不产生警告,如果开启此模式,则带 0 的时间会插入 ‘0000-00-00’ ,并产生警告。
如果开启此模式和严格模式,不允许 0,insert 过程报错,除非使用 ignore。
(4)PAD_CHAR_TO_FULL_LENGTH
8.0 版本中已过时,计划删除。
-
默认情况下,检索时会把 CHAR 类型列值中的空格 trim 掉
如果开启此模式,则不会去除 CHAR 列的空格,并且以全长填充。这个模式只适用于 CHAR 格式的列。mysql> CREATE TABLE t1 (c1 CHAR(10)); Query OK, 0 rows affected (0.37 sec) mysql> INSERT INTO t1 (c1) VALUES('xy'); Query OK, 1 row affected (0.01 sec) mysql> SET sql_mode = ''; Query OK, 0 rows affected (0.00 sec) mysql> SELECT c1, CHAR_LENGTH(c1) FROM t1; +------+-----------------+ | c1 | CHAR_LENGTH(c1) | +------+-----------------+ | xy | 2 | +------+-----------------+ 1 row in set (0.00 sec) mysql> SET sql_mode = 'PAD_CHAR_TO_FULL_LENGTH'; Query OK, 0 rows affected (0.00 sec) mysql> SELECT c1, CHAR_LENGTH(c1) FROM t1; +------------+-----------------+ | c1 | CHAR_LENGTH(c1) | +------------+-----------------+ | xy | 10 | +------------+-----------------+ 1 row in set (0.00 sec)
9. 已删除的模式
(1)NO_AUTO_CREATE_USER
8.0 版本已删除。
- 不自动创建用户,此模式默认开启
对于 grant 语句,除非指定了身份验证信息,否则应防止 grant 语句自动创建新的用户。grant 创建用户的方法已过时,推荐使用 create user。可以使用 identified by 指定非空密码,使用 identified with 指定身份验证插件。
(2)NO_FIELD_OPTIONS
8.0 版本已删除。
- 不要在
SHOW CREATE TABLE的输出中打印 MySQL 特定的列选项
(3)NO_KEY_OPTIONS
8.0 版本已删除。
- 不要在
SHOW CREATE TABLE的输出中打印 MySQL 特定的索引选项
(4)NO_TABLE_OPTIONS
8.0 版本已删除。
- 不要在
SHOW CREATE TABLE的输出中打印 MySQL 特定的表选项 (例如 ENGINE)
三、组合模式
1. 标准模式
(1)ANSI
-
最重要的模式。此模式更改语法和行为以更符合标准 SQL。
等效于 REAL_AS_FLOAT、PIPES_AS_CONCAT、ANSI_QUOTES、IGNORE_SPACE、ONLY_FULL_GROUP_BY。 -
ANSI 模式还会导致服务器返回查询错误,其中无法将具有外部引用 S(outer_ref) 的集合函数 S 聚合到已解决外部引用的外部查询中
SELECT * FROM t1 WHERE t1.a IN (SELECT MAX(t1.b) FROM t2 WHERE ...);
2. 传统模式
(1)TRADITIONAL
- 当在列中插入不正确的值时,报错,而不是警告。
等效于 STRICT_TRANS_TABLES、STRICT_ALL_TABLES、NO_AUTO_CREATE_USER、和 NO_ENGINE_SUBSTITUTION。- 启用传统模式后,一旦发生错误,INSERT 或 UPDATE 将中止。
- 如果使用的是非事务性存储引擎,错误之前可能不会进行回滚,导致部分完成更新。
3. 已删除的模式
(1)DB2
等效于 PIPES_AS_CONCAT、ANSI_QUOTES、IGNORE_SPACE、NO_KEY_OPTIONS、NO_TABLE_OPTIONS、NO_FIELD_OPTIONS。
8.0 版本已删除。
(2)MAXDB
等效于 PIPES_AS_CONCAT、ANSI_QUOTES、IGNORE_SPACE、NO_KEY_OPTIONS、NO_TABLE_OPTIONS、NO_FIELD_OPTIONS、NO_AUTO_CREATE_USER。
8.0 版本已删除。
(3)MSSQL
等效于 PIPES_AS_CONCAT、ANSI_QUOTES、IGNORE_SPACE、NO_KEY_OPTIONS、NO_TABLE_OPTIONS、NO_FIELD_OPTIONS。
8.0 版本已删除。
(4)MYSQL323
等效于 MYSQL323、HIGH_NOT_PRECEDENCE。
8.0 版本已删除。
(5)MYSQL40
等效于 MYSQL40、HIGH_NOT_PRECEDENCE。
8.0 版本已删除。
(6)ORACLE
等效于 PIPES_AS_CONCAT、ANSI_QUOTES、IGNORE_SPACE、NO_KEY_OPTIONS、NO_TABLE_OPTIONS、NO_FIELD_OPTIONS、NO_AUTO_CREATE_USER。
8.0 版本已删除。
(7)POSTGRESQL
等效于 PIPES_AS_CONCAT、ANSI_QUOTES、IGNORE_SPACE、NO_KEY_OPTIONS、NO_TABLE_OPTIONS、NO_FIELD_OPTIONS。
8.0 版本已删除。
四、严格模式
严格模式控制在数据变更操作(insert/update)时,怎么处理无效或缺失值。
1. 严格模式的处理
- 正常的严格模式对无效值产生错误,而不是警告
可以通过 INSERT IGNORE 或 UPDATE IGNORE,使 MySQL 为无效或缺失的值插入调整后的值并产生警告。 - IGNORE 关键字和严格模式
- ignore 将错误降级为警告
- 严格模式,将警告升级为错误
- 当同时使用 IGNORE 关键字和严格的 SQL 模式时,IGNORE 优先
2. 启用严格模式
- 开启 STRICT_ALL_TABLES 或 STRICT_TRANS_TABLES,则启用严格模式
- 严格模式包括
这 3 个模式已过时,将来可能会和严格模式合并。- ERROR_FOR_DIVISION_BY_ZERO
- NO_ZERO_DATE
- NO_ZERO_IN_DATE