MySQL 5.7 SQL MODE嚴格模式帶來的影響

背景:在以前的升級過程當中,爲了對RD更加友好的支持,咱們都是把MySQL的SQL MODE修改爲低版本的,可是這樣每每也會帶來一些其餘問題,今天咱們就來梳理一下,SQL MODE在MySQL 5.6和5.7兩個版本之間的差別,讓DBA在後續的升級過程當中,更加從容。html

1、如何查看如今MySQL的SQL MODE

1.1  查看mysql的sql mode:

# MySQL 5.6 Default SQL_MODE;

mysql> select @@sql_mode;

+--------------------------------------------+

| @@sql_mode                                 |

+--------------------------------------------+

| NO_ENGINE_SUBSTITUTION |

+--------------------------------------------+

1 row in set (0.00 sec)



# MySQL 5.7 Default SQL_MODE;

mysql> select @@sql_mode;

+-------------------------------------------------------------------------------------------------------------------------------------------+

| @@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)

1.2  簡單解釋下5.7的默認SQL MODE

  • ONLY_FULL_GROUP_BY

在select、having、group by列表裏引用的列必須在group by列表中,不然報錯。mysql

  • STRICT_TRANS_TABLES

嚴格模式控制MySQL如何處理非法或丟失的輸入值的SQL。有幾種緣由可使一個值爲非法。例如,數據類型錯誤或超出範圍。當新插入的行不包含某列的沒有顯示定義DEFAULT子句的值,則該值被丟失。sql

對於事務表,當啓用STRICT_ALL_TABLES或STRICT_TRANS_TABLES模式時,若是語句中有非法或丟失值,則會出現錯誤。SQL語句被回滾。ui

對於非事務表,STRICT_TRANS_TABLES,MySQL將非法值轉換爲最接近該列的合法值並插入調整後的值。若是值丟失,MySQL在列中插入隱式 默認值。在任何狀況下,MySQL都會生成警告而不是給出錯誤並繼續執行語句。this

若是你不使用嚴格模式(即不啓用STRICT_TRANS_TABLES或STRICT_ALL_TABLES模式),對於非法或丟失的值,MySQL將插入調整後的值並給出警告。在嚴格模式,你能夠經過INSERT IGNORE或UPDATE IGNORE來實現。spa

  • NO_ZERO_IN_DATE

在嚴格模式,不接受月或日部分爲0的日期,對年不限制。若是使用IGNORE選項,咱們爲相似的日期插入’0000-00-00’。在非嚴格模式,能夠接受該日期,但會生成警告。code

  • NO_ZERO_DATE

在嚴格模式,不要將’0000-00-00’作爲合法日期。你仍然能夠用IGNORE選項插入零日期。在非嚴格模式,能夠接受該日期,但會生成警告。orm

  • ERROR_FOR_DIVISION_BY_ZERO

在嚴格模式,在INSERT或UPDATE過程當中,若是被零除(或MOD(X,0)),則產生錯誤(不然爲警告)。若是未給出該模式,被零除時MySQL返回NULL。若是用到INSERT IGNORE或UPDATE IGNORE中,MySQL生成被零除警告,但操做結果爲NULL。htm

  • NO_AUTO_CREATE_USER

在嚴格模式下,防止GRANT自動建立新用戶,除非還指定了密碼。blog

2、驗證SQL MODE對SQL的影響

2.1  group by語句的影響

mysql> select @@sql_mode;
+-------------------------------------------+
| @@sql_mode                                |
+-------------------------------------------+
| ONLY_FULL_GROUP_BY,NO_ENGINE_SUBSTITUTION |
+-------------------------------------------+
1 row in set (0.00 sec)

mysql> select * from words group by id;
ERROR 1055 (42000): 'tom.words.word' isn't in GROUP BY

mysql> select id,count(word) from words group by word;
ERROR 1055 (42000): 'tom.words.id' isn't in GROUP BY

mysql> select id,word,count(word) from words group by word;
ERROR 1055 (42000): 'tom.words.id' isn't in GROUP BY

mysql> select word,count(word) from words group by word having count(word)>1;
+------+-------------+
| word | count(word) |
+------+-------------+
| bbbb |           3 |
+------+-------------+
1 row in set (0.00 sec)

mysql> select * from words;
+----+------+
| id | word |
+----+------+
|  1 | aaaa |
|  2 | bbbb |
|  3 | cccc |
|  4 | bbbb |
|  5 | bbbb |
+----+------+
5 rows in set (0.00 sec)

2.2  日期零值的影響

mysql> CREATE TABLE `test` (`time` datetime NOT NULL DEFAULT '1111-00-01 00:00:00'  ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
Query OK, 0 rows affected (0.00 sec)

mysql> select @@sql_mode;
+-------------------------------------------+
| @@sql_mode                                |
+-------------------------------------------+
| ONLY_FULL_GROUP_BY,NO_ENGINE_SUBSTITUTION |
+-------------------------------------------+
1 row in set (0.00 sec)

mysql> set sql_mode='ONLY_FULL_GROUP_BY,NO_ENGINE_SUBSTITUTION,NO_ZERO_IN_DATE,NO_ZERO_DATE';
Query OK, 0 rows affected, 2 warnings (0.00 sec)

mysql> drop table test;
Query OK, 0 rows affected (0.00 sec)

mysql> CREATE TABLE `test` (`time` datetime NOT NULL DEFAULT '1111-00-01 00:00:00'  ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
Query OK, 0 rows affected, 1 warning (0.01 sec)

mysql> select @@sql_mode;
+------------------------------------------------------------------------+
| @@sql_mode                                                             |
+------------------------------------------------------------------------+
| ONLY_FULL_GROUP_BY,NO_ZERO_IN_DATE,NO_ZERO_DATE,NO_ENGINE_SUBSTITUTION |
+------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> select @@version;
+------------+
| @@version  |
+------------+
| 5.6.35-log |
+------------+
1 row in set (0.00 sec)

mysql> set 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';
Query OK, 0 rows affected, 3 warnings (0.00 sec)

mysql> drop table test;
Query OK, 0 rows affected (0.00 sec)

mysql> CREATE TABLE `test` (`time` datetime NOT NULL DEFAULT '1111-00-01 00:00:00'  ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
ERROR 1067 (42000): Invalid default value for 'time'

下面的例子:

# 不合法默認值;

CREATE TABLE `test` (`time` datetime NOT NULL DEFAULT '1111-00-01 00:00:00'

) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;



# 合法默認值;

CREATE TABLE `test` (`time` datetime NOT NULL DEFAULT '1111-01-01 00:00:00'

) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;



# 合法默認值;

CREATE TABLE `test` (`time` datetime NOT NULL DEFAULT '0000-01-01 00:00:00'

) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

2.3  字段非法值的影響

mysql> select @@sql_mode;
+------------------------+
| @@sql_mode             |
+------------------------+
| NO_ENGINE_SUBSTITUTION |
+------------------------+
1 row in set (0.00 sec)

mysql> set 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';
Query OK, 0 rows affected, 3 warnings (0.00 sec)

mysql> use tom
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> show create table words;
+-------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table                                                                                                                                                                |
+-------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| words | CREATE TABLE `words` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `word` varchar(4) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8 |
+-------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> insert into words(word) values("abcde");
ERROR 1406 (22001): Data too long for column 'word' at row 1
mysql> insert into words(word) values(10/0);
ERROR 1365 (22012): Division by 0
mysql> insert into words(word) values(13);
Query OK, 1 row affected (0.00 sec)

mysql> insert into words(id) values(abc);
ERROR 1054 (42S22): Unknown column 'abc' in 'field list'

mysql> insert into words(id) values('abc');
ERROR 1366 (HY000): Incorrect integer value: 'abc' for column 'id' at row 1

mysql> alter table words change `word` `word` varchar(3) DEFAULT NULL;
ERROR 1265 (01000): Data truncated for column 'word' at row 1

mysql> alter table words change `id` `id` int(10) unsigned NOT NULL AUTO_INCREMENT;
Query OK, 7 rows affected (0.01 sec)
Records: 7  Duplicates: 0  Warnings: 0

mysql> insert into words(id) values(-1);
ERROR 1264 (22003): Out of range value for column 'id' at row 1

mysql>show create table words1;
+--------+---------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table  | Create Table                                                                                                                                            |
+--------+---------------------------------------------------------------------------------------------------------------------------------------------------------+
| words1 | CREATE TABLE `words1` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `word` varchar(5) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
+--------+---------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> insert into words1(id) values(1);
ERROR 1364 (HY000): Field 'word' doesn't have a default value

2.4  非嚴格模式致使的sql執行結果不一致

##表數據以下
mysql> select * from words;
+----+------+
| id | word |
+----+------+
|  7 | aaaa |
|  8 | bbbb |
|  9 | cccc |
| 10 | bbbb |
| 11 | cccc |
+----+------+
5 rows in set (0.00 sec)

##5.6環境執行
mysql> select * from (select * from words order by id desc) a group by word  ;
+----+------+
| id | word |
+----+------+
|  7 | aaaa |
| 10 | bbbb |
| 11 | cccc |
+----+------+
3 rows in set (0.00 sec)

##5.7環境執行
mysql> select * from (select * from words order by id desc) a group by word  ;
+----+------+
| id | word |
+----+------+
|  7 | aaaa |
|  8 | bbbb |
|  9 | cccc |
+----+------+
3 rows in set (0.00 sec)

##正確的姿式
mysql> select max(id),word from (select * from words order by id desc) a group by word  ;
+---------+------+
| max(id) | word |
+---------+------+
|       7 | aaaa |
|      10 | bbbb |
|      11 | cccc |
+---------+------+
3 rows in set (0.00 sec)

官方鏈接:點我查看

相關文章
相關標籤/搜索