MySQL(十三)------ SQL Mode相關問題

         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 “關鍵字,這樣就能獲得通用的建表腳本。對於沒有列出來的別的數據庫,在進行遷移時可以靈活組合模式以滿足要求。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章