MySQL可以在不同的SQL模式下运行,这样,我们可以通过修改SQL模式来达到数据校验、迁移等功能。
一、常用的SQL模式
选中某种模式,其实是一系列模式的组合,这样就可以将多种不同功能的原子模式进行组合得到想要的功能。
二、SQL Mode简介
在MySQL中,SQL Mode常用来解决下面几类问题:
- 通过设置SQL Mode,完成不同严格程度的数据校验,有效的保障数据准确性;
- 通过设置SQL Mode为ANSI模式,来保证大多数SQL符合标准SQL语法,这样在不同数据库之间进行迁移时不需要对业务SQL进行较大的修改;
- 在不同数据库之间进行数据迁移之前,可以通过设置SQL Mode使数据更方便的迁移到别的数据库中。
下面举个例子看一下;
当前数据库的模式为严格模式
mysql> select @@sql_mode;
+----------------------------------------------------------------+
| @@sql_mode |
+----------------------------------------------------------------+
| STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION |
+----------------------------------------------------------------+
1 row in set (0.00 sec)
如果此时向数据库表中的name varchar(5)字段插入超过5的值,那么会引发错误;
但如果不是严格模式,那此时它会将超过的部分截断后插入,不会出错但是会有一个警告。
设置SQL Mode模式的方式:
1、 使用”--sql-mode=“modes”“选项在MySQL启动时设置;
2、 使用语句SET[SESSION|GLOBAL]sql_mode=”modes“修改,其中SESSION表示只在本次连接中生效,GLOBAL则表示本次连接不生效,对于新的连接则生效;
三、SQL Mode常见功能
1. 检验日期数据的合法性
SQL Mode模式改为ANSI,向日期字段插入一个有误的数据,发现该模式下可以插入,但数值都变成了0
mysql> set session sql_mode='ANSI';
Query OK, 0 rows affected, 1 warning (0.00 sec)
mysql> create table t (d datetime);
Query OK, 0 rows affected (0.01 sec)
mysql> insert into t values('2018-04-31');
Query OK, 1 row affected, 1 warning (0.01 sec)
mysql> select * from t;
+---------------------+
| d |
+---------------------+
| 0000-00-00 00:00:00 |
+---------------------+
1 row in set (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)
当将SQL Mode改为传统模式(也属于严格模式)时,报错,无法插入
mysql> set session sql_mode='traditional';
Query OK, 0 rows affected, 1 warning (0.00 sec)
mysql> insert into t values('2018-04-31');
ERROR 1292 (22007): Incorrect datetime value: '2018-04-31' for column 'd' at row 1
mysql> select @@sql_mode;
+------------------------------------------------------------------------------------------------------------------------------------------------------+
| @@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)
2. 检验运算的合法性
比如如果在INSERT 或 UPDATE 过程中,如在严格模式下运行MOD(X, 0)则会报错,但是在非严格模式下MOD(X, 0)的结果为NULL;
下面展示了在不同模式下插入数值的情况:
mysql> set session sql_mode='ANSI';
Query OK, 0 rows affected, 1 warning (0.00 sec)
mysql> create table t (i int);
Query OK, 0 rows affected (0.02 sec)
mysql> insert into t values(9%0);
Query OK, 1 row affected (0.00 sec)
mysql> select * from t;
+------+
| i |
+------+
| NULL |
+------+
1 row in set (0.00 sec)
mysql> set session sql_mode='traditional';
Query OK, 0 rows affected, 1 warning (0.00 sec)
mysql> insert into t values(9%0);
ERROR 1365 (22012): Division by 0
3. 启用NO_BACKSLASH_ESCAPES模式使反斜线成为普通字符
mysql> set session sql_mode='ANSI';
Query OK, 0 rows affected, 1 warning (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> create table t (context varchar(20));
Query OK, 0 rows affected (0.02 sec)
mysql> insert into t values('\beijing');
Query OK, 1 row affected (0.00 sec)
mysql> select * from t;
+---------+
| context |
+---------+
|eijing |
+---------+
1 row in set (0.00 sec)
mysql> insert into t values('\\beijing');
Query OK, 1 row affected (0.00 sec)
mysql> select * from t;
+----------+
| context |
+----------+
|eijing |
| \beijing |
+----------+
2 rows in set (0.00 sec)
mysql> set sql_mode=' REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ONLY_FULL_GROUP_BY,ANSI,NO_BACKSLASH_ESCAPES';
ERROR 1231 (42000): Variable 'sql_mode' can't be set to the value of ' REAL_AS_FLOAT'
mysql> set sql_mode='REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ONLY_FULL_GROUP_BY,ANSI,NO_BACKSLASH_ESCAPES';
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,NO_BACKSLASH_ESCAPES |
+-----------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> insert into t values('\\beijing');
Query OK, 1 row affected (0.00 sec)
mysql> select * from t;
+-----------+
| context |
+-----------+
|eijing |
| \beijing |
| \\beijing |
+-----------+
3 rows in set (0.00 sec)
通过这个例子可以看出当ANSI增加了NO_BACKSLASH_ESCAPES模式后,反斜线变成普通字符,如果导入的数据存在反斜线则需要使用这种模式确保数据的正确性。
4. 启用PIPES_AS_CONCAT模式
该模式将 ” || “视为字符串连接操作符,这样就可以将两个用” || “连接起来的字符串合并,因为在Oracle等数据库中是使用” || “作为字符串连接操作符的,因此如果将Oracle数据库数据导入到MySQL的话就需要用这种模式来支持这种操作。
ANSI模式下包含了PIPES_AS_CONCAT模式:
mysql> set session 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 'beijing' || '2008';
+---------------------+
| 'beijing' || '2008' |
+---------------------+
| beijing2008 |
+---------------------+
1 row in set (0.00 sec)
四、如何在数据迁移中选择合适的SQL Mode模式
MySQL在与其它异构数据库之间如果有数据迁移的需求,那么就需要选择好对应的模式组合确保数据能够安全、完整的迁移到目标数据库;MySQL本身已经做了这方面的工作,它与一些常用数据库之间进行迁移时的模式组合已经搭配好,如下表所示:
数据迁移时,NO_TABLE_OPTIONS模式可以去掉建表语句中的” engine “关键字,这样就能获得通用的建表脚本。对于没有列出来的别的数据库,在进行迁移时可以灵活组合模式以满足要求。