MySQL 服务器可以在不同的 SQL modes下运行,并且可以针对不同的客户端以不同的方式应用这些模式,具体取决于 sql_mode 系统变量的值。 DBA 可以设置全局 SQL 模式,每个会话也可以设置session级别的 SQL 模式。
模式会影响 MySQL 支持的 SQL 语法以及它执行的数据验证检查。 在我们实际运维工作中,迁移、新增节点或升级等操作后由于sql_mode的不同导致业务异常的情况还是常出现的问题。
这篇文章基于MySQL8.0版本对MySQL支持的sql_mode及其定义做一个简单的介绍;在MySQL8.0版本中默认使用严格模式,SQL mode是: ONLY_FULL_GROUP_BY, STRICT_TRANS_TABLES, NO_ZERO_IN_DATE, NO_ZERO_DATE, ERROR_FOR_DIVISION_BY_ZERO, and NO_ENGINE_SUBSTITUTION。
1、查看和修改sql_mode
设置sql_mode可以在启动时追加参数–sql-mode=“modes” 或 在配置文件中指定 sql-mode=“modes"重启生效。如果想清空sql_mode配置可以在启动时追加参数–sql-mode=”" 或 在配置文件中指定 sql-mode=""重启生效。
也可在运行时修改sql_mode。修改GLOBAL变量需要SYSTEM_VARIABLES_ADMIN 或 SUPER 权限,影响新建连接的操作。每个连接都能修改session级别的变量;
SET GLOBAL sql_mode = 'modes'; SET SESSION sql_mode = 'modes'; SELECT @@GLOBAL.sql_mode; SELECT @@SESSION.sql_mode;#建表报错 mysql> create table t_par(id int unsigned) -> partition by range(id-100)( -> partition p0 values less than(-50), -> partition p1 values less than(0), -> partition p2 values less than(10), -> partition p3 values less than(maxvalue) ERROR 1563 (HY000): Partition constant is out of partition function domain #修改环境变量后可以正常创建并插入数据 mysql> SET sql_mode='NO_UNSIGNED_SUBTRACTION'; Query OK, 0 rows affected (0.00 sec) mysql> select @@sql_mode; +-------------------------+ | @@sql_mode | +-------------------------+ | NO_UNSIGNED_SUBTRACTION | +-------------------------+ 1 row in set (0.00 sec) mysql> create table t_par(id int unsigned) partition by range(id-100)( partition p0 values less than(-50), partition p1 values less than(0), partition p2 values less than(10), partition p3 values less than(maxvalue) ); Query OK, 0 rows affected (0.24 sec) mysql> insert into t_par values (1),(10),(100); Query OK, 3 rows affected (0.03 sec) Records: 3 Duplicates: 0 Warnings: 0 mysql> select * from t_par; +------+ | id | +------+ | 1 | | 10 | | 100 | +------+ 3 rows in set (0.01 sec) ##如果这时我们修改了sql_mode,分区表就无法查询和插入,原表中的数据就不可见了;重新设置sql_mode后恢复; mysql> SET sql_mode=''; Query OK, 0 rows affected (0.00 sec) mysql> select @@sql_mode; +------------+ | @@sql_mode | +------------+ | | +------------+ 1 row in set (0.00 sec) mysql> select * from t_par; ERROR 1563 (HY000): Partition constant is out of partition function domain mysql> insert into t_par values (11); ERROR 1563 (HY000): Partition constant is out of partition function domain mysql> SET sql_mode='NO_UNSIGNED_SUBTRACTION'; Query OK, 0 rows affected (0.00 sec) mysql> insert into t_par values (11); Query OK, 1 row affected (0.01 sec) mysql> select * from t_par; +------+ | id | +------+ | 1 | | 10 | | 5 | | 15 | | 12 | | 11 | | 100 | +------+ 7 rows in set (0.01 sec)
2、sql_mode简介
MySQL支持的sql_mode较多,其中包含两个组合sql mode,分别是 ANSI 和 TRADITIONAL,分别代表特定的一组sql_mode。包含两个特殊的sql_mode,STRICT_TRANS_TABLES 和 STRICT_ALL_TABLES,开启任意一个模式后MySQL将会进入严格模式(Strict mode),严格模式具体的影响后面会详细介绍。
本节简单介绍下各sql_mode的作用
此模式更改语法和行为以更符合标准 SQL。是一种特殊组合模式,等效于REAL_AS_FLOAT、PIPES_AS_CONCAT、ANSI_QUOTES、IGNORE_SPACE 和 ONLY_FULL_GROUP_BY。
mysql> set sql_mode="ANSI"; Query OK, 0 rows affected (0.00 sec) mysql> select @@sql_mode; +--------------------------------------------------------------------------------+ | @@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> select * from t2; +------+ | id | +------+ | 1 | | 3 | | 4 | +------+ 3 rows in set (0.00 sec) mysql> select * from t2 a where a.id in (select min(a.id) from t2); ERROR 1111 (HY000): Invalid use of group function mysql> SET sql_mode=''; Query OK, 0 rows affected (0.00 sec) mysql> select * from t2 a where a.id in (select min(a.id) from t2); +------+ | id | +------+ | 1 | | 3 | | 4 | +------+ 3 rows in set (0.00 sec) ---------------- ---------------- mysql> create table t_mode(id int ,name varchar(64),age int); Query OK, 0 rows affected (0.09 sec) mysql> insert into t_mode values(1,'lee',68),(2,'kat',23),(3,'yunly',38); Query OK, 3 rows affected (0.02 sec) Records: 3 Duplicates: 0 Warnings: 0 mysql> select max(age),name from t_mode group by id; +----------+-------+ | max(age) | name | +----------+-------+ | 68 | lee | | 23 | kat | | 38 | yunly | +----------+-------+ 3 rows in set (0.01 sec) mysql> SET sql_mode='ANSI'; Query OK, 0 rows affected (0.00 sec) mysql> select max(age),name from t_mode group by id; ERROR 1055 (42000): Expression #2 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'test.t_mode.name' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
STRICT_TRANS_TABLES 为事务存储引擎启用严格的 SQL 模式,并在可能的情况下为非事务存储引擎启用。在 STRICT_TRANS_TABLES模式下对于支持事务的数据库引擎的表(如Innodb表),DML语句中的非法或缺失值会导致错误,该语句将被终止,该事务将被回滚;对于非事务引擎的表(如MyISAM表)如非法或缺失值在第一行中语句将终止,表数据不变。如果出现在非第一行时,MySQL 将无效值转换为最接近该列的有效值并插入调整后的值。如果缺少值,MySQL 会为列数据类型插入隐式默认值。并返回一个warning。
#创建一个事务表 mysql> create table t_mode_t (c1 int ,c2 varchar(32) not null) engine=innodb; Query OK, 0 rows affected (0.07 sec) #创建一个非事务表 mysql> create table t_mode_nt (c1 int ,c2 varchar(32) not null) engine=myisam; Query OK, 0 rows affected (0.01 sec) #设置STRICT_TRANS_TABLES模式 mysql> set sql_mode="STRICT_TRANS_TABLES"; Query OK, 0 rows affected, 1 warning (0.00 sec) mysql> select @@sql_mode; +---------------------+ | @@sql_mode | +---------------------+ | STRICT_TRANS_TABLES | +---------------------+ 1 row in set (0.00 sec) mysql> insert into t_mode_t values (1,null); ERROR 1048 (23000): Column 'c2' cannot be null mysql> insert into t_mode_t values ("ccc","ccc"); ERROR 1366 (HY000): Incorrect integer value: 'ccc' for column 'c1' at row 1 #对于事务表无论非法数值在第几行都会报错并回滚 mysql> insert into t_mode_t values (1,"ccc"),(2,null); ERROR 1048 (23000): Column 'c2' cannot be null #对非事务表无论当非法数值在第一行会报错并回滚,当非法数值在第二行时会转换成合法的值并返回错误。在本例中null转换成'',string转换成0 mysql> insert into t_mode_nt values ("ccc","ccc"); ERROR 1366 (HY000): Incorrect integer value: 'ccc' for column 'c1' at row 1 mysql> insert into t_mode_nt values (1,"ccc"),("ccc",null); Query OK, 2 rows affected, 2 warnings (0.00 sec) Records: 2 Duplicates: 0 Warnings: 2 mysql> show warnings; +---------+------+---------------------------------------------------------+ | Level | Code | Message | +---------+------+---------------------------------------------------------+ | Warning | 1366 | Incorrect integer value: 'ccc' for column 'c1' at row 2 | | Warning | 1048 | Column 'c2' cannot be null | +---------+------+---------------------------------------------------------+ 2 rows in set (0.00 sec) mysql> select * from t_mode_nt; +------+-----+ | c1 | c2 | +------+-----+ | 1 | ccc | | 0 | | +------+-----+ 2 rows in set (0.00 sec) #取消STRICT_TRANS_TABLES模式后, 当违反非空约束的值在第一行时报错并回滚,其他情况下事务表和非事务表都执行成功并返回warning mysql> set sql_mode=""; Query OK, 0 rows affected (0.00 sec) mysql> select @@sql_mode; +------------+ | @@sql_mode | +------------+ | | +------------+ 1 row in set (0.00 sec) mysql> insert into t_mode_t values (1,null); ERROR 1048 (23000): Column 'c2' cannot be null mysql> insert into t_mode_t values ("ccc","ccc"); Query OK, 1 row affected, 1 warning (0.01 sec) mysql> insert into t_mode_t values (1,"ccc"),(2,null); Query OK, 2 rows affected, 1 warning (0.00 sec) Records: 2 Duplicates: 0 Warnings: 1 mysql> show warnings; +---------+------+----------------------------+ | Level | Code | Message | +---------+------+----------------------------+ | Warning | 1048 | Column 'c2' cannot be null | +---------+------+----------------------------+ 1 row in set (0.00 sec) mysql> insert into t_mode_nt values ("ccc","ccc"); Query OK, 1 row affected, 1 warning (0.00 sec) mysql> insert into t_mode_nt values (1,"ccc"),("ccc",null); Query OK, 2 rows affected, 2 warnings (0.00 sec) Records: 2 Duplicates: 0 Warnings: 2
STRICT_ALL_TABLES 同STRICT_TRANS_TABLES模式基本一致,区别在于对于非事务引擎的表,当非法或缺失值在第二或之后的行时将返回错误并忽略其他行。这时前面的行数据已被插入或修改,出现了部分更新的情况;
mysql> set sql_mode="STRICT_ALL_TABLES"; Query OK, 0 rows affected, 1 warning (0.01 sec) mysql> select @@sql_mode; +-------------------+ | @@sql_mode | +-------------------+ | STRICT_ALL_TABLES | +-------------------+ 1 row in set (0.00 sec) mysql> truncate table t_mode_nt; Query OK, 0 rows affected (0.01 sec) mysql> insert into t_mode_nt values (1,"ccc"),("ccc",null); ERROR 1366 (HY000): Incorrect integer value: 'ccc' for column 'c1' at row 2 #发生部分更新的问题 mysql> select * from t_mode_nt; +------+-----+ | c1 | c2 | +------+-----+ | 1 | ccc | +------+-----+ 1 row in set (0.00 sec)
TRADITIONAL 一种特殊的组合模式,等价于 STRICT_TRANS_TABLES, STRICT_ALL_TABLES, NO_ZERO_IN_DATE, NO_ZERO_DATE, ERROR_FOR_DIVISION_BY_ZERO, and NO_ENGINE_SUBSTITUTION。设置TRADITIONAL模式使得MySQL使用起来表现的像一个“传统”数据库。简单来说在TRADITIONAL模式下插入不正确的值时会导致错误而不是返回warning。这一特点是需要注意的,如前面介绍的STRICT_ALL_TABLES模式的情况,当使用非事务引擎的表时可能会导致部分更新的情况发生。
mysql> set sql_mode="TRADITIONAL"; Query OK, 0 rows affected (0.00 sec) mysql> select @@sql_mode\G *************************** 1. row *************************** @@sql_mode: STRICT_TRANS_TABLES,STRICT_ALL_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,TRADITIONAL,NO_ENGINE_SUBSTITUTION 1 row in set (0.00 sec)
ALLOW_INVALID_DATES 适用于date和datetime列,该模式下不对日期做全面检查,仅检查月份在1-12,日期在1-31之间;启用ALLOW_INVALID_DATES后可以存储4月31,2月31日等日期数据;
该模式不应用于timestamp列,timestamp类型始终需要一个有效的时间
mysql> set sql_mode="ALLOW_INVALID_DATES"; Query OK, 0 rows affected (0.01 sec) mysql> select @@sql_mode; +---------------------+ | @@sql_mode | +---------------------+ | ALLOW_INVALID_DATES | +---------------------+ 1 row in set (0.00 sec) mysql> insert into t_mode_date values('0000-4-31','2021-2-31 19:00:00'); Query OK, 1 row affected (0.01 sec) mysql> select * from t_mode_date; +------------+---------------------+ | c1 | c2 | +------------+---------------------+ | 0000-04-31 | 2021-02-31 19:00:00 | +------------+---------------------+ 1 row in set (0.00 sec)
ANSI_QUOTES 在该模式下将双引号视为标识符引号(" 等价于 `),不能使用双引号来引用文字字符串;
mysql> set sql_mode="ANSI_QUOTES"; Query OK, 0 rows affected (0.00 sec) mysql> select @@sql_mode; +-------------+ | @@sql_mode | +-------------+ | ANSI_QUOTES | +-------------+ 1 row in set (0.00 sec) mysql> select "string"; ERROR 1054 (42S22): Unknown column 'string' in 'field list' mysql> show create table t_mode_t\G *************************** 1. row *************************** Table: t_mode_t Create Table: CREATE TABLE "t_mode_t" ( "c1" int DEFAULT NULL, "c2" varchar(32) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci 1 row in set (0.00 sec) #禁用ansi_quotes模式后的区别 mysql> set sql_mode=""; Query OK, 0 rows affected (0.00 sec) mysql> select "string"; +--------+ | string | +--------+ | string | +--------+ 1 row in set (0.00 sec) mysql> show create table t_mode_t\G *************************** 1. row *************************** Table: t_mode_t Create Table: CREATE TABLE `t_mode_t` ( `c1` int DEFAULT NULL, `c2` varchar(32) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci 1 row in set (0.00 sec)
ERROR_FOR_DIVISION_BY_ZERO 该模式下 n/0,n%0会返回null 并产生告警,应和严格模式搭配使用;
mysql> set sql_mode="ERROR_FOR_DIVISION_BY_ZERO"; Query OK, 0 rows affected, 1 warning (0.00 sec) mysql> show warnings; +---------+------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Level | Code | Message | +---------+------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Warning | 3135 | 'NO_ZERO_DATE', 'NO_ZERO_IN_DATE' and 'ERROR_FOR_DIVISION_BY_ZERO' sql modes should be used with strict mode. They will be merged with strict mode in a future release. | +---------+------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 1 row in set (0.00 sec) mysql> select 5/0; +------+ | 5/0 | +------+ | NULL | +------+ 1 row in set, 1 warning (0.01 sec) mysql> select 5%0; +------+ | 5%0 | +------+ | NULL | +------+ 1 row in set, 1 warning (0.00 sec) mysql> show warnings; +---------+------+---------------+ | Level | Code | Message | +---------+------+---------------+ | Warning | 1365 | Division by 0 | +---------+------+---------------+ 1 row in set (0.00 sec) mysql> set sql_mode=""; Query OK, 0 rows affected (0.00 sec) mysql> select 5%0; +------+ | 5%0 | +------+ | NULL | +------+ 1 row in set (0.00 sec) mysql> select 5/0; +------+ | 5/0 | +------+ | NULL | +------+ 1 row in set (0.00 sec)
HIGH_NOT_PRECEDENCE 该模式提升NOT运算符的优先级
mysql> set sql_mode="HIGH_NOT_PRECEDENCE"; Query OK, 0 rows affected (0.01 sec) #(not 1) between -5 and 5 ===> 0 between -5 and 5 ===> 1 mysql> select not 1 between -5 and 5; +------------------------+ | not 1 between -5 and 5 | +------------------------+ | 1 | +------------------------+ 1 row in set (0.00 sec) mysql> set sql_mode=""; Query OK, 0 rows affected (0.00 sec) # not (1 between -5 and 5) ====> not 1 ===> 0 mysql> select not 1 between -5 and 5; +------------------------+ | not 1 between -5 and 5 | +------------------------+ | 0 | +------------------------+ 1 row in set (0.00 sec)
IGNORE_SPACE 允许内置函数名和 ( 间存在空格。副作用是MySQL根据部分内置函数和 ( 之间是否有空格来区分该名称是函数调用 还是 对表名或列名 标识符的非表达式引用,对于这类内置函数名 在该模式下 将转变成保留字。ascii这类内置函数不受影响。
mysql> set sql_mode="ignore_space"; Query OK, 0 rows affected (0.00 sec) mysql> select sum (id) from test.t1; +----------+ | sum (id) | +----------+ | 23361 | +----------+ 1 row in set (0.00 sec) mysql> create table sum (id int); ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'sum (id int)' at line 1 mysql> select ascii ('a'); +-------------+ | ascii ('a') | +-------------+ | 97 | +-------------+ 1 row in set (0.00 sec) mysql> create table ascii (id int); Query OK, 0 rows affected (0.05 sec) ---------------------- mysql> set sql_mode=""; Query OK, 0 rows affected (0.00 sec) mysql> select sum (id) from test.t1; ERROR 1630 (42000): FUNCTION ymh.sum does not exist. Check the 'Function Name Parsing and Resolution' section in the Reference Manual mysql> create table sum (id int); Query OK, 0 rows affected (0.11 sec) mysql> select ascii ('a'); +-------------+ | ascii ('a') | +-------------+ | 97 | +-------------+ 1 row in set (0.00 sec) mysql> create table ascii (id int); Query OK, 0 rows affected (0.05 sec)
受影响的内置函数列表
ADDDATE BIT_AND BIT_OR BIT_XOR COUNT CURDATE CURTIME DATE_ADD DATE_SUB EXTRACT GROUP_CONCAT POSITION SESSION_USER STDDEV STDDEV_POP STDDEV_SAMP SUBDATE SUBSTR SUBSTRING SYSDATE SYSTEM_USER VARIANCE VAR_POP VAR_SAMP
NO_AUTO_VALUE_ON_ZERO 该模式影响auto_increment列,该模式下只能插入null生成下一个序号。禁用NO_AUTO_VALUE_ON_ZERO模式插入0 和 null都会生成下一个序号。如果0已存储在表中,当使用逻辑备份导入数据时 需要指定NO_AUTO_VALUE_ON_ZERO,否则数据将会不一致;
mysql> create table t_mode_ai (id int not null auto_increment primary key); Query OK, 0 rows affected (0.12 sec) mysql> set sql_mode="NO_AUTO_VALUE_ON_ZERO"; Query OK, 0 rows affected (0.00 sec) mysql> insert into t_mode_ai values (null); Query OK, 1 row affected (0.01 sec) mysql> insert into t_mode_ai values (0); Query OK, 1 row affected (0.01 sec) mysql> select * from t_mode_ai; +----+ | id | +----+ | 0 | | 1 | +----+ 2 rows in set (0.01 sec) mysql> insert into t_mode_ai values (0); ERROR 1062 (23000): Duplicate entry '0' for key 't_mode_ai.PRIMARY' mysql> set sql_mode=""; Query OK, 0 rows affected (0.00 sec) mysql> insert into t_mode_ai values (null); Query OK, 1 row affected (0.01 sec) mysql> insert into t_mode_ai values (0); Query OK, 1 row affected (0.01 sec) mysql> select * from t_mode_ai; +----+ | id | +----+ | 1 | | 2 | +----+ 2 rows in set (0.00 sec) mysql> insert into t_mode_ai values (0); Query OK, 1 row affected (0.00 sec)
NO_BACKSLASH_ESCAPES 该模式会禁用反斜杠 \ 作为转义字符。
mysql> set sql_mode="NO_BACKSLASH_ESCAPES"; Query OK, 0 rows affected (0.00 sec) mysql> select "ab\\nc"; +--------+ | ab\\nc | +--------+ | ab\\nc | +--------+ 1 row in set (0.00 sec) mysql> select "ab\nc"; +-------+ | ab\nc | +-------+ | ab\nc | +-------+ 1 row in set (0.01 sec) mysql> set sql_mode=""; Query OK, 0 rows affected (0.00 sec) mysql> select "ab\\nc"; +-------+ | ab\nc | +-------+ | ab\nc | +-------+ 1 row in set (0.00 sec) mysql> select "ab\nc"; +------+ +------+ +------+ 1 row in set (0.00 sec)
NO_DIR_IN_CREATE 该模式下创建表时,忽略所有 INDEX DIRECTORY 和 DATA DIRECTORY 指令。此选项在从服务器上很有用。
mysql> set sql_mode="NO_DIR_IN_CREATE"; Query OK, 0 rows affected (0.00 sec) mysql> create table t1 (id int) DATA DIRECTORY = "/data/mysql/log"; Query OK, 0 rows affected, 1 warning (0.06 sec) mysql> show warnings; +---------+------+---------------------------------+ | Level | Code | Message | +---------+------+---------------------------------+ | Warning | 1618 | <DATA DIRECTORY> option ignored | +---------+------+---------------------------------+ 1 row in set (0.00 sec) mysql> set sql_mode=""; Query OK, 0 rows affected (0.00 sec) mysql> create table t2 (id int) DATA DIRECTORY = "/data/mysql/log"; Query OK, 0 rows affected (0.06 sec) mysql> system ls -lth /data/mysql/log/ymh/*.ibd -rw-r----- 1 mysql mysql 112K Sep 8 16:44 /data/mysql/log/ymh/t2.ibd
NO_ENGINE_SUBSTITUTION 该模式下create table 或 alter table时指定的引擎是disabled or not compiled in的时候会报错,语句不会执行;禁用该模式后create table 会替换成default engine并返回warning。alter table会返回warning,修改engine的语句不会执行。
mysql> set sql_mode="no_engine_substitution"; Query OK, 0 rows affected (0.00 sec) mysql> create table t2(id int) engine=ABC; ERROR 1286 (42000): Unknown storage engine 'ABC' mysql> alter table t1 add name varchar(255),engine=ABC; ERROR 1286 (42000): Unknown storage engine 'ABC' mysql> set sql_mode=""; Query OK, 0 rows affected (0.00 sec) mysql> create table t3(id int) engine=ABC; Query OK, 0 rows affected, 2 warnings (0.06 sec) mysql> show warnings; +---------+------+--------------------------------------------+ | Level | Code | Message | +---------+------+--------------------------------------------+ | Warning | 1286 | Unknown storage engine 'ABC' | | Warning | 1266 | Using storage engine InnoDB for table 't3' | +---------+------+--------------------------------------------+ 2 rows in set (0.00 sec) mysql> alter table t3 add name varchar(255),engine=BCD; Query OK, 0 rows affected, 1 warning (0.09 sec) Records: 0 Duplicates: 0 Warnings: 1 mysql> show warnings; +---------+------+------------------------------+ | Level | Code | Message | +---------+------+------------------------------+ | Warning | 1286 | Unknown storage engine 'BCD' | +---------+------+------------------------------+ 1 row in set (0.00 sec) mysql> show create table t3\G *************************** 1. row *************************** Table: t3 Create Table: CREATE TABLE `t3` ( `id` int DEFAULT NULL, `name` varchar(255) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci 1 row in set (0.00 sec)
NO_UNSIGNED_SUBTRACTION 有无符号数参与的整数间的减法会产生一个无符号的结果,如果结果为负会报错;在NO_UNSIGNED_SUBTRACTION模式下减法的结果将是有符号的,且负数结果插入无符号列会被裁剪为0并返回warning;
mysql> create table t11 (id int unsigned); Query OK, 0 rows affected (0.07 sec) mysql> create table t12 (id int unsigned); Query OK, 0 rows affected (0.05 sec) mysql> insert into t11 values (1); Query OK, 1 row affected (0.01 sec) mysql> insert into t12 values (10); Query OK, 1 row affected (0.01 sec) mysql> set sql_mode="NO_UNSIGNED_SUBTRACTION"; Query OK, 0 rows affected (0.00 sec) mysql> select t11.id - t12.id from t11,t12; +-----------------+ | t11.id - t12.id | +-----------------+ | -9 | +-----------------+ 1 row in set (0.00 sec) mysql> insert into t11 select t11.id - t12.id from t11,t12; Query OK, 1 row affected, 1 warning (0.01 sec) Records: 1 Duplicates: 0 Warnings: 1 mysql> show warnings; +---------+------+---------------------------------------------+ | Level | Code | Message | +---------+------+---------------------------------------------+ | Warning | 1264 | Out of range value for column 'id' at row 1 | +---------+------+---------------------------------------------+ 1 row in set (0.00 sec) mysql> select * from t11; +------+ | id | +------+ | 1 | | 0 | +------+ 2 rows in set (0.00 sec) ---------- mysql> set sql_mode=""; Query OK, 0 rows affected (0.00 sec) mysql> select t11.id - t12.id from t11,t12; ERROR 1690 (22003): BIGINT UNSIGNED value is out of range in '(`ymh`.`t11`.`id` - `ymh`.`t12`.`id`)'
NO_ZERO_DATE 该模式下不允许“0000-00-00”作为有效日期,插入会产生告警;通常和严格模式一同使用
mysql> create table t_date(c1 date); Query OK, 0 rows affected (0.05 sec) mysql> set sql_mode="no_zero_date"; Query OK, 0 rows affected, 1 warning (0.00 sec) mysql> insert into t_date values ("0000-00-00"); Query OK, 1 row affected, 1 warning (0.01 sec) mysql> show warnings; +---------+------+---------------------------------------------+ | Level | Code | Message | +---------+------+---------------------------------------------+ | Warning | 1264 | Out of range value for column 'c1' at row 1 | +---------+------+---------------------------------------------+ 1 row in set (0.01 sec) ------------- mysql> set sql_mode=""; Query OK, 0 rows affected (0.00 sec) mysql> insert into t_date values ("0000-00-00"); Query OK, 1 row affected (0.00 sec)
NO_ZERO_IN_DATE 该模式下不允许月 或者 日部分出现0 的日期,插入会转换成“0000-00-00” 并产生告警;通常和严格模式一起使用
mysql> set sql_mode="no_zero_in_date"; Query OK, 0 rows affected, 1 warning (0.00 sec) mysql> insert into t_date values ("2021-00-11"); Query OK, 1 row affected, 1 warning (0.01 sec) mysql> insert into t_date values ("2021-11-00"); Query OK, 1 row affected, 1 warning (0.00 sec) mysql> insert into t_date values ("2021-00-00"); Query OK, 1 row affected, 1 warning (0.00 sec) mysql> select * from t_date; +------------+ | c1 | +------------+ | 0000-00-00 | | 0000-00-00 | | 0000-00-00 | +------------+ 3 rows in set (0.00 sec)
ONLY_FULL_GROUP_BY 该模式下当select、having和order by的字段使用了非聚合列,也就是既不在group by子句中引用或命名,也不是一个聚合函数时将会拒绝执行;
mysql> set sql_mode="only_full_group_by"; Query OK, 0 rows affected (0.00 sec) mysql> select c1,c2 from t_mode_t group by c2; ERROR 1055 (42000): Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'ymh.t_mode_t.c1' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by --------------- mysql> set sql_mode=""; Query OK, 0 rows affected (0.00 sec) mysql> select c1,c2 from t_mode_t group by c2; +------+-----+ | c1 | c2 | +------+-----+ | 0 | ccc | | 2 | | +------+-----+ 2 rows in set (0.00 sec)
PAD_CHAR_TO_FULL_LENGTH 该模式下 char(N)字段类型将会在尾部填充空格到其全长;
注意:禁用该模式将会裁剪char字段的尾部空格;
varchar字段总是保留尾部空格,如果有的话;
mysql> create table t_char(c1 char(10),c2 varchar(10)); Query OK, 0 rows affected (0.28 sec) mysql> set sql_mode='PAD_CHAR_TO_FULL_LENGTH'; Query OK, 0 rows affected, 1 warning (0.00 sec) mysql> insert into t_char value ('a ','a '); Query OK, 1 row affected (0.01 sec) mysql> select char_length(c1),char_length(c2) from t_char; +-----------------+-----------------+ | char_length(c1) | char_length(c2) | +-----------------+-----------------+ | 10 | 2 | +-----------------+-----------------+ 1 row in set (0.01 sec) mysql> mysql> truncate table t_char; Query OK, 0 rows affected (0.06 sec) mysql> mysql> set sql_mode=""; Query OK, 0 rows affected (0.00 sec) mysql> insert into t_char value ('a ','a '); Query OK, 1 row affected (0.01 sec) mysql> select char_length(c1),char_length(c2) from t_char; +-----------------+-----------------+ | char_length(c1) | char_length(c2) | +-----------------+-----------------+ | 1 | 2 | +-----------------+-----------------+ 1 row in set (0.01 sec)
PIPES_AS_CONCAT 该模式下将||作为字符串连接运算符(和concat()函数相同),而不是作为or的同义词
mysql> set sql_mode="pipes_as_concat"; Query OK, 0 rows affected (0.00 sec) mysql> select 1||0; +------+ | 1||0 | +------+ | 10 | +------+ 1 row in set (0.00 sec) mysql> set sql_mode="" mysql> mysql> set sql_mode=""; Query OK, 0 rows affected (0.00 sec) mysql> select 1||0; +------+ | 1||0 | +------+ | 1 | +------+ 1 row in set, 1 warning (0.00 sec) mysql> mysql> mysql> mysql> mysql> mysql> set sql_mode="pipes_as_concat"; Query OK, 0 rows affected (0.00 sec) mysql> select "abc"||"def"; +--------------+ | "abc"||"def" | +--------------+ | abcdef | +--------------+ 1 row in set (0.00 sec) mysql> select true||false; +-------------+ | true||false | +-------------+ | 10 | +-------------+ 1 row in set (0.00 sec) mysql> mysql> set sql_mode=""; Query OK, 0 rows affected (0.00 sec) mysql> select "abc"||"def"; +--------------+ | "abc"||"def" | +--------------+ | 0 | +--------------+ 1 row in set, 3 warnings (0.01 sec) mysql> show warnings; +---------+------+-------------------------------------------------------------------------------------------------------+ | Level | Code | Message | +---------+------+-------------------------------------------------------------------------------------------------------+ | Warning | 1287 | '|| as a synonym for OR' is deprecated and will be removed in a future release. Please use OR instead | | Warning | 1292 | Truncated incorrect DOUBLE value: 'abc' | | Warning | 1292 | Truncated incorrect DOUBLE value: 'def' | +---------+------+-------------------------------------------------------------------------------------------------------+ 3 rows in set (0.00 sec) mysql> select true||false; +-------------+ | true||false | +-------------+ | 1 | +-------------+ 1 row in set, 1 warning (0.00 sec) mysql> show warnings; +---------+------+-------------------------------------------------------------------------------------------------------+ | Level | Code | Message | +---------+------+-------------------------------------------------------------------------------------------------------+ | Warning | 1287 | '|| as a synonym for OR' is deprecated and will be removed in a future release. Please use OR instead | +---------+------+-------------------------------------------------------------------------------------------------------+ 1 row in set (0.00 sec)
REAL_AS_FLOAT 该模式将real作为float的同义词;禁用该模式MySQL将real作为double的同义词
mysql> set sql_mode="real_as_float"; Query OK, 0 rows affected (0.01 sec) mysql> create table t_real(c1 real); Query OK, 0 rows affected (0.05 sec) mysql> desc t_real; +-------+-------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+-------+------+-----+---------+-------+ | c1 | float | YES | | NULL | | +-------+-------+------+-----+---------+-------+ 1 row in set (0.04 sec) mysql> set sql_mode=""; Query OK, 0 rows affected (0.00 sec) mysql> create table t_real_1(c1 real); Query OK, 0 rows affected (0.05 sec) mysql> desc t_real_1; +-------+--------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+--------+------+-----+---------+-------+ | c1 | double | YES | | NULL | | +-------+--------+------+-----+---------+-------+ 1 row in set (0.01 sec)
TIME_TRUNCATE_FRACTIONAL 控制对date、datetime、timestamp类型的带小数部分的秒的处理,该模式下会根据精度截断。默认会做四舍五入。
mysql> create table t_time_fractional(c1 time(2),c2 datetime(2),c3 timestamp(2)); Query OK, 0 rows affected (0.06 sec) mysql> set sql_mode="time_truncate_fractional"; Query OK, 0 rows affected (0.00 sec) mysql> insert into t_time_fractional values (10.999,"2021-08-08 08:08:08.999","2021-08-08 08:08:08.999"); Query OK, 1 row affected (0.01 sec) mysql> select * from t_time_fractional; +-------------+------------------------+------------------------+ | c1 | c2 | c3 | +-------------+------------------------+------------------------+ | 00:00:10.99 | 2021-08-08 08:08:08.99 | 2021-08-08 08:08:08.99 | +-------------+------------------------+------------------------+ 1 row in set (0.00 sec) ------------------ mysql> truncate table t_time_fractional; Query OK, 0 rows affected (0.06 sec) ------------------ mysql> set sql_mode=""; Query OK, 0 rows affected (0.00 sec) mysql> insert into t_time_fractional values (10.999,"2021-08-08 08:08:08.999","2021-08-08 08:08:08.999"); Query OK, 1 row affected (0.01 sec) mysql> select * from t_time_fractional; +-------------+------------------------+------------------------+ | c1 | c2 | c3 | +-------------+------------------------+------------------------+ | 00:00:11.00 | 2021-08-08 08:08:09.00 | 2021-08-08 08:08:09.00 | +-------------+------------------------+------------------------+ 1 row in set (0.00 sec)
3、严格模式(Strict SQL Mode)
启用了 STRICT_ALL_TABLES 或 STRICT_TRANS_TABLES 则进入严格模式,这两个模式的效果有差别,详见第二节中的描述。严格模式下对DML中存在无效或缺失值将报错,除非使用 insert ignore 或者 update ignore语法。select中的无效值会返回warning。
严格模式适用于以下语句
ALTER TABLE CREATE TABLE CREATE TABLE ... SELECT DELETE (both single table and multiple table) INSERT LOAD DATA LOAD XML SELECT SLEEP() UPDATE (both single table and multiple table)
注意:对于存过和函数,如果程序是在严格模式生效时定义的,则调用该函数时上述语句将按严格模式执行,无论当前是否禁用严格模式;反之亦然 mysql> set sql_mode="STRICT_TRANS_TABLES"; Query OK, 0 rows affected, 1 warning (0.00 sec) mysql> select @@sql_mode; +---------------------+ | @@sql_mode | +---------------------+ | STRICT_TRANS_TABLES | +---------------------+ 1 row in set (0.00 sec) mysql> delimiter // mysql> CREATE PROCEDURE p_mode(p1 INT) -> BEGIN -> insert into ta values(p1); -> END; delimiter ; Query OK, 0 rows affected (0.01 sec) mysql> delimiter ; mysql> create table ta(id int unsigned); Query OK, 0 rows affected (0.06 sec) mysql> call p_mode(5); Query OK, 1 row affected (0.01 sec) mysql> select * from ta; +------+ | id | +------+ | 5 | +------+ 1 row in set (0.00 sec) mysql> call p_mode(-5); ERROR 1264 (22003): Out of range value for column 'id' at row 1 #禁用严格模式后依然报错 mysql> set sql_mode=""; Query OK, 0 rows affected (0.00 sec) mysql> call p_mode(-5); ERROR 1264 (22003): Out of range value for column 'id' at row 1 ------------------------------------- 在非严格模式下创建PROCEDURE并执行,插入负数到无符号整型会返回warning 并插入0 mysql> drop PROCEDURE p_mode; Query OK, 0 rows affected (0.01 sec) mysql> set sql_mode=""; Query OK, 0 rows affected (0.00 sec) mysql> delimiter // mysql> CREATE PROCEDURE p_mode(p1 INT) -> BEGIN -> insert into ta values(p1); -> END; Query OK, 0 rows affected (0.01 sec) mysql> delimiter ; mysql> truncate table ta; Query OK, 0 rows affected (0.07 sec) mysql> call p_mode(-5); Query OK, 1 row affected, 1 warning (0.01 sec) mysql> select * from ta; +------+ | id | +------+ | 0 | +------+ 1 row in set (0.00 sec) #设置严格模式后,再次执行 返回warnings并插入0。这时直接执行insert语句会报错 mysql> set sql_mode="STRICT_TRANS_TABLES"; Query OK, 0 rows affected, 1 warning (0.00 sec) mysql> call p_mode(-5); Query OK, 1 row affected, 1 warning (0.00 sec) mysql> show warnings; +---------+------+---------------------------------------------+ | Level | Code | Message | +---------+------+---------------------------------------------+ | Warning | 1264 | Out of range value for column 'id' at row 1 | +---------+------+---------------------------------------------+ 1 row in set (0.00 sec) mysql> insert into ta values(-5); ERROR 1264 (22003): Out of range value for column 'id' at row 1 #创建时sql_mode可以在information_schema.routines视图中查询 mysql> select ROUTINE_NAME,ROUTINE_TYPE,SQL_MODE from information_schema.routines where routine_name='p_mode'; +--------------+--------------+-----------------------------------------------------------------------------+ | ROUTINE_NAME | ROUTINE_TYPE | SQL_MODE | +--------------+--------------+-----------------------------------------------------------------------------+ | p_mode | PROCEDURE | STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO | +--------------+--------------+-----------------------------------------------------------------------------+ 1 row in set (0.00 sec)
支持ignore关键字的语句包括
CREATE TABLE … SELECT: 如果插入到由 SELECT 生成的表中时,键值冲突的hang被丢弃。 DELETE: 删除过程中忽略错误 INSERT:唯一键冲突的行被丢弃,非法或缺失值会修改为最接近的有效值(LOAD DATA、LOAD XML) UPDATE:不会更新导致唯一键冲突的行,非法或缺失值会修改为最接近的有效值; #对于找不到与给定值匹配的分区的分区表INSERT语句,IGNORE将返回warning且不会成功插入该行数据,但不影响其他行的插入 mysql> create table t_par(id int) -> partition by range(id)( -> partition p1 values less than(0), -> partition p2 values less than(10) ); Query OK, 0 rows affected (0.16 sec) mysql> insert into t_par values(11); ERROR 1526 (HY000): Table has no partition for value 11 mysql> insert into t_par values(11),(9); ERROR 1526 (HY000): Table has no partition for value 11 ------------------------------ mysql> insert ignore into t_par values(11); Query OK, 0 rows affected, 1 warning (0.00 sec) mysql> show warnings; +---------+------+-------------------------------------+ | Level | Code | Message | +---------+------+-------------------------------------+ | Warning | 1526 | Table has no partition for value 11 | +---------+------+-------------------------------------+ 1 row in set (0.00 sec) mysql> select * from t_par; Empty set (0.00 sec) mysql> insert ignore into t_par values(11),(9); Query OK, 1 row affected, 1 warning (0.00 sec) Records: 2 Duplicates: 1 Warnings: 1 mysql> select * from t_par; +------+ | id | +------+ | 9 |