導語
mysql服務器可以在不同的sql_mode模式下運行,並且可以根據sql_mode系統變量的值,為不同的客戶機應用不同的模式。sql_mode會影響mysql支持的sql語法,並且會執行數據驗證檢查,那不同的mysql_mode是如何影響支持的sql語法和數據類型檢驗的呢?本文將給大家系統總結分析說明這個問題,以及在sql_mode為空的時候,測試需要注意的測試點。
目錄 一、經典的“測試缺陷” 二、Server SQL Modes 介紹 2.1 sql_mode概念 2.2 七種常見的sql_mode ONLY_FULL_GROUP_BY ANSI_QUOTES PIPES_AS_CONCAT NO_AUTO_CREATE_USER NO_ZERO_DATE NO_ZERO_IN_DATE STRICT_TRANS_TABLES 2.3 三個重要且常用的sql_mode模式 ANSI STRICT_TRANS_TABLES TRADITIONA 三、測試策略 3.1 小結:寬松,嚴格模式下常見sql實例影響和結果對比 3.2 寬松模式下測試應該注意哪些地方?
一:經典的“測試缺陷”
某日版本一如常規的發布上線,灰度過程中開發傳來了一個“噩耗”,程序在操作數據庫時有bug。
開發:第一筆數據插入都正常,第二筆數據插入就報重入了,報主鍵沖突,而實際這兩筆數據的主鍵應該是不一樣的,后面發現第一筆數據插入時的主鍵值是錯誤導致的。
測試:怎么會,在日志里打印的每條sql語句我都是有檢查的,測試環境插入多少遍都從沒報過這樣的錯誤,而且每筆數據插入完成后,DB表的主鍵,業務關鍵字段也都是有檢查的,當時我測試的記錄還在,主鍵肯定是插入對的。
開發:在日志里查看sql語句的主鍵值是對的,但是插入db的主鍵是被截斷的錯誤的。截斷入庫還不報error錯誤,是因為線上mysql的sql_mode為空是寬松模式,導致數據插入時被截斷,也不報error錯誤,業務層會以為是插入成功業務繼續,從而產生了這個問題。
被截斷的字段是表的主鍵,表的主鍵在DB設計長度是64,在應用程序中,主鍵= 批次號+訂單號,批次號是外部系統返回字段,訂單號由內部產生。測試同學在模塊測試過程中,默認設置批次號為20190717_02,訂單號長度固定為32位,主鍵一直都只有43位,遠小於64位,該問題一直被隱藏;而后跟外部聯調時,實際傳入的批次號是34位,聯調時已經出現主鍵被截斷的錯誤,但是由於設置的sql_mode為空,數據截斷插入不報error錯誤,流程正常運行結束,而且聯調時又一筆通過導致問題在測試階段被雪藏。
二:Server SQL Modes 介紹
2.1 sql_mode概念
sql_mode定義了MySQL應該支持的sql語法,對數據值和類型的校驗程度。不同的sql_mode模式下導致mysql服務器在運行時結果不一樣,有兩種方式可以改變該值:
方法一:靜態修改,修改配置文件(mysql的安裝目錄下my.ini)后重啟mysql。
方法二:動態修改,sql_mode支持全局修改以及會話層修改。
全局查看和全局修改:SELECT @@GLOBAL.sql_mode; SET @@global.sql_mode= 'modes';全局修改就是影響整個數據庫。需要超級特權(root)才能修改,global修改后,需要重新連接進來才會生效。
會話查看和會話修改:SELECT @@SESSION.sql_mode;
SET SESSION sql_mode = 'modes';
會話就是影響當前連接的會話,如果會話終止,設置的參數值失效。
2.2 sql_mode常用值說明
SQL Mode 定義了兩個方面:MySQL應支持的SQL語法,以及應該在數據上執行何種確認檢查。
2.2.1 常見的SQL語法支持類
為了更好的舉例說明不同的sql_mode對不同sql的影響,使用表t_payfund_log_20181121,其結構定義如下:
CREATE TABLE `t_payfund_log_20181121` ( `Fbank_seq` varchar(32) NOT NULL, `Flast_bank_seq` varchar(32) DEFAULT NULL, `Fstatus` smallint(6) NOT NULL DEFAULT '0', `Fuid` bigint(20) DEFAULT NULL, `Flast_interface_id` int(11) DEFAULT NULL, PRIMARY KEY (`Fbank_seq`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
表內數據暫且包含以下三條記錄:
類型一:ONLY_FULL_GROUP_BY
ONLY_FULL_GROUP_BY
對於GROUP BY聚合操作,如果在SELECT中的列、HAVING或者ORDER BY子句的列,沒有在GROUP BY中出現,那么這個SQL是不合法的。是可以理解的,因為不在 group by 的列查出來展示會有矛盾。在5.7中默認啟用,所以DB在從5.6升級到5.7的過程需要注意。
示例SQL語句:SELECT Fstatus, Flast_interface_id FROM t_payfund_log_20181121 GROUP BY Fstatus;
A: sql_mode 為空:查詢結果后正常返回
MySQL [loleina]> SELECT @@GLOBAL.sql_mode; +-------------------+ | @@GLOBAL.sql_mode | +-------------------+ | | +-------------------+ 1 row in set (0.00 sec) MySQL [loleina]> SELECT Fstatus, Flast_interface_id FROM t_payfund_log_20181121 GROUP BY Fstatus; +---------+--------------------+ | Fstatus | Flast_interface_id | +---------+--------------------+ | 2 | 998501 | | 3 | 99802 | +---------+--------------------+ 2 rows in set (0.00 sec)
B: sql_mode=‘ONLY_FULL_GROUP_BY ':查詢直接報錯 (設置后重新連接登錄)
MySQL [loleina]> SET GLOBAL sql_mode ='ONLY_FULL_GROUP_BY'; Query OK, 0 rows affected (0.00 sec) MySQL [loleina]> SELECT @@GLOBAL.sql_mode; +--------------------+ | @@GLOBAL.sql_mode | +--------------------+ | ONLY_FULL_GROUP_BY | +--------------------+ 1 row in set (0.00 sec) MySQL [loleina]> SELECT Fstatus, Flast_interface_id FROM t_payfund_log_20181121 GROUP BY Fstatus; ERROR 1055 (42000): Expression #2 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'loleina.t_payfund_log_20181121.Flast_interface_id' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
當遇到這種情況,可以使用ANY_VALUE(),對於不符合ONLY_FULL_GROUP_BY的字段使用ANY_VALUE()函數,讓MySQL跳過ONLY_FULL_GROUP_BY檢測
MySQL [loleina]> SELECT Fstatus, ANY_VALUE(Flast_interface_id) FROM t_payfund_log_20181121 GROUP BY Fstatus; +---------+-------------------------------+ | Fstatus | ANY_VALUE(Flast_interface_id) | +---------+-------------------------------+ | 2 | 998501 | | 3 | 99802 | +---------+-------------------------------+ 2 rows in set (0.00 sec)
類型二:ANSI_QUOTES
啟用 ANSI_QUOTES 后,不能用雙引號來引用字符串,因為它被解釋為識別符,作用與 ` 一樣。
示例SQL語句: update t_payfund_log_20181121 set Flast_bank_seq="20" where Fbank_seq='23020190915090211222002';
A: sql_mode 為空:查詢結果后正常返回.
MySQL [loleina]> SELECT @@GLOBAL.sql_mode; +-------------------+ | @@GLOBAL.sql_mode | +-------------------+ | | +-------------------+ 1 row in set (0.00 sec) MySQL [loleina]> update t_payfund_log_20181121 set Flast_bank_seq="20" where Fbank_seq='23020190915090211222001'; Query OK, 1 row affected (0.01 sec) Rows matched: 1 Changed: 1 Warnings: 0
B: sql_mode=‘ANSI_QUOTES ':報 Unknown column '' in 'field list語法錯誤.
MySQL [loleina]> SELECT @@GLOBAL.sql_mode; +-------------------+ | @@GLOBAL.sql_mode | +-------------------+ | ANSI_QUOTES | +-------------------+ 1 row in set (0.00 sec) MySQL [loleina]> update t_payfund_log_20181121 set Flast_bank_seq="20" where Fbank_seq='23020190915090211222002'; ERROR 1054 (42S22): Unknown column '20' in 'field list'
類型三:PIPES_AS_CONCAT
在Oracle,||連接字符串的,而在mysql,是用CONCAT來實現的,mysql為了兼容這一個特性,設置了這個模式,mysql會將 將 ||
視為字符串的連接操作符而非 或 運算符,
示例SQL語句:update t_payfund_log_20181121 set Fmemo=' '||Fmemo||'&pay failed' where Fbank_seq='23020190915090211222002';
等價於: update t_payfund_log_20181121 set Fmemo=CONCAT_WS(' ',Fmemo, '&pay failed') where Fbank_seq='23020190915090211222002';
A: sql_mode 為空:使用||報數據截斷warnings
MySQL [loleina]> SELECT @@GLOBAL.sql_mode; +-------------------+ | @@GLOBAL.sql_mode | +-------------------+ | | +-------------------+ 1 row in set (0.00 sec) MySQL [loleina]> update t_payfund_log_20181121 set Fmemo=' '||Fmemo||'&pay failed' where Fbank_seq='23020190915090211222002'; Query OK, 1 row affected, 2 warnings (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 2 MySQL [loleina]> show warnings; +---------+------+-------------------------------------------------+ | Level | Code | Message | +---------+------+-------------------------------------------------+ | Warning | 1292 | Truncated incorrect DOUBLE value: ' test2 ' | | Warning | 1292 | Truncated incorrect DOUBLE value: '&pay failed' | +---------+------+-------------------------------------------------+ 2 rows in set (0.01 sec) MySQL [loleina]> SELECT * from loleina.t_payfund_log_20181121; +-------------------------+----------------+---------+--------+--------------------+--------------------+ | Fbank_seq | Flast_bank_seq | Fstatus | Fuid | Flast_interface_id | Fmemo | +-------------------------+----------------+---------+--------+--------------------+--------------------+ | 23020190915090211222001 | 2 | 2 | 540977 | 998501 | test1 &pay failed | | 23020190915090211222002 | 2 | 3 | 34566 | 99802 | 0 | | 23020190915090211222003 | 3 | 2 | 44322 | 997601 | test3 | +-------------------------+----------------+---------+--------+--------------------+--------------------+ 3 rows in set (0.00 sec)
B: sql_mode=‘PIPES_AS_CONCAT ':數據正常插入。
MySQL [loleina]> SELECT @@GLOBAL.sql_mode; +-------------------+
| @@GLOBAL.sql_mode |
+-------------------+
| PIPES_AS_CONCAT |
+-------------------+
1 row in set (0.00 sec) MySQL [loleina]> SELECT * from loleina.t_payfund_log_20181121; +-------------------------+----------------+---------+--------+--------------------+---------+
| Fbank_seq | Flast_bank_seq | Fstatus | Fuid | Flast_interface_id | Fmemo |
+-------------------------+----------------+---------+--------+--------------------+---------+
| 23020190915090211222001 | 2 | 2 | 540977 | 998501 | test1 |
| 23020190915090211222002 | 2 | 3 | 34566 | 99802 | test2 |
| 23020190915090211222003 | 3 | 2 | 44322 | 997601 | test3 |
+-------------------------+----------------+---------+--------+--------------------+---------+
3 rows in set (0.00 sec) MySQL [loleina]> update t_payfund_log_20181121 set Fmemo=' '||Fmemo||'&pay failed' where Fbank_seq='23020190915090211222001'; Query OK, 1 row affected (0.01 sec) Rows matched: 1 Changed: 1 Warnings: 0 MySQL [loleina]> SELECT * from loleina.t_payfund_log_20181121; +-------------------------+----------------+---------+--------+--------------------+--------------------+
| Fbank_seq | Flast_bank_seq | Fstatus | Fuid | Flast_interface_id | Fmemo |
+-------------------------+----------------+---------+--------+--------------------+--------------------+
| 23020190915090211222001 | 2 | 2 | 540977 | 998501 | test1 &pay failed |
| 23020190915090211222002 | 2 | 3 | 34566 | 99802 | test2 |
| 23020190915090211222003 | 3 | 2 | 44322 | 997601 | test3 |
+-------------------------+----------------+---------+--------+--------------------+--------------------+
3 rows in set (0.00 sec)
類型四:NO_AUTO_CREATE_USER
授權命令GRANT 語句的語法:GRANT privileges (columns) ON what TO user IDENTIFIED BY "password" WITH GRANT OPTION,其中IDENTIFIED BY是可選子句,用來於指定mysql用戶的口令。既然是可選,那就是可以沒有。如果沒有指定IDENTIFIED BY,會怎么樣?
答案是可能導致一個安全漏洞:如果user是指現有用戶,那么沒有任何影響;但如果user是指新用戶,那么該用戶將不被賦予口令,這樣的語句會創建一個口令為空,且已有數據庫操作權限的mysql用戶!那怎么辦呢?mysql自身已經給出了解決方案,將sql_mode設置為NO_AUTO_CREATE_USER,mysql會阻止任何創建空密碼的用戶。
NO_AUTO_CREATE_USER
字面意思不自動創建用戶。在給MySQL用戶授權時,我們習慣使用上述類似命令 GRANT ... ON ... TO dbuser
一起創建用戶。設置該模式后就與oracle操作類似,授權之前須先建立用戶,否則會報error錯誤。5.7.7開始也默認了。
示例SQL語句:GRANT ALL PRIVILEGES ON *.* TO 'root'@'10.43.24.149';
A: sql_mode 為空:存在安全漏洞
MySQL [loleina]> SELECT @@GLOBAL.sql_mode; +-------------------+ | @@GLOBAL.sql_mode | +-------------------+ | | +-------------------+ 1 row in set (0.00 sec) MySQL [loleina]> GRANT ALL PRIVILEGES ON *.* TO 'root'@'10.43.24.149'; Query OK, 0 rows affected, 1 warning (0.00 sec) MySQL [loleina]> GRANT ALL PRIVILEGES ON *.* TO 'root'@'10.43.24.19'; Query OK, 0 rows affected, 1 warning (0.00 sec)
B: sql_mode=‘NO_AUTO_CREATE_USER ':設置該模式:直接報錯
MySQL [loleina]> SELECT @@GLOBAL.sql_mode; +---------------------+
| @@GLOBAL.sql_mode |
+---------------------+
| NO_AUTO_CREATE_USER |
+---------------------+
1 row in set (0.00 sec) MySQL [loleina]> GRANT ALL PRIVILEGES ON *.* TO 'root'@'10.43.24.149'; ERROR 1133 (42000): Can't find any matching row in the user table
MySQL [(none)]> GRANT ALL PRIVILEGES ON *.* TO 'root'@'10.43.24.22'; ERROR 1133 (42000): Can't find any matching row in the user table
那如果使用IDENTIFIED BY子句呢?雖然不會直接報error錯誤,但是waring錯誤已經明確給出”不贊成使用grant創建新用戶,會在以后的版本中刪除。使用CREATEUSER語句創建新用戶。”
MySQL [(none)]> GRANT ALL PRIVILEGES ON *.* TO 'root'@'10.43.24.22' IDENTIFIED BY 'root1234' WITH GRANT OPTION; Query OK, 0 rows affected, 1 warning (0.00 sec) MySQL [(none)]> show warnings; +---------+------+------------------------------------------------------------------------------------------------------------------------------------+
| Level | Code | Message |
+---------+------+------------------------------------------------------------------------------------------------------------------------------------+
| Warning | 1287 | Using GRANT for creating new user is deprecated and will be removed in future release. Create new user with CREATE USER statement. |
+---------+------+------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
2.2.2 數據檢查類
類型五:NO_ZERO_DATE
NO_ZERO_DATE
認為日期 '0000-00-00' 是否非法,與是否設置后面的嚴格模式有關。 在嚴格模式,只有‘0000-00-00’報錯,輸入‘1000-00-00’不報錯; 在非嚴格模式,可以接受該‘0000-00-00’但會生成警告。
示例sql語句:
INSERT INTO `loleina`.`t_payfund_log_20181121` (`Fbank_seq`, `Fuid`, `Flast_interface_id`, `Fpay_time`, `Fend_time`) VALUES ('23020190915090211222009', '540977', '998501', 0000-00-00, 0000-00-00);
A: sql_mode 為空:'0000-00-00' 能合法插入
MySQL [loleina]> SELECT @@GLOBAL.sql_mode; +-------------------+ | @@GLOBAL.sql_mode | +-------------------+ | | +-------------------+ 1 row in set (0.00 sec) MySQL [loleina]> INSERT INTO `loleina`.`t_payfund_log_20181121` (`Fbank_seq`, `Fuid`, `Flast_interface_id`, `Fpay_time`, `Fend_time`) VALUES ('23020190915090211222006', '540977', '998501', 0000-00-00, 0000-00-00); Query OK, 1 row affected (0.00 sec) MySQL [loleina]> select * from t_payfund_log_20181121 \g +-------------------------+----------------+---------+--------+--------------------+--------------------+----------------------------+---------------------+ | Fbank_seq | Flast_bank_seq | Fstatus | Fuid | Flast_interface_id | Fmemo | Fpay_time | Fend_time | +-------------------------+----------------+---------+--------+--------------------+--------------------+----------------------------+---------------------+ | 23020190915090211222001 | 2 | 2 | 540977 | 998501 | test1 &pay failed | NULL | NULL | | 23020190915090211222002 | 2 | 3 | 34566 | 99802 | 0 | NULL | NULL | | 23020190915090211222003 | 3 | 2 | 44322 | 997601 | test3 | NULL | NULL | | 23020190915090211222006 | NULL | 0 | 540977 | 998501 | NULL | 0000-00-00 00:00:00.000000 | 0000-00-00 00:00:00 | +-------------------------+----------------+---------+--------+--------------------+--------------------+----------------------------+---------------------+ 4 rows in set (0.00 sec)
B: 非嚴格模式設置sql_mode =‘NO_ZERO_DATE
’,輸入日期=‘0000-00-00'’ 能插入但會產生告警信息:
MySQL [loleina]> SELECT @@GLOBAL.sql_mode; +-------------------+
| @@GLOBAL.sql_mode |
+-------------------+
| NO_ZERO_DATE |
+-------------------+
1 row in set (0.00 sec) MySQL [loleina]> INSERT INTO `loleina`.`t_payfund_log_20181121` (`Fbank_seq`, `Fuid`, `Flast_interface_id`, `Fpay_time`, `Fend_time`) VALUES ('23020190915090211222008', '540977', '998501', 0000-00-00, 0000-00-00); Query OK, 1 row affected, 2 warnings (0.00 sec) MySQL [loleina]> show warnings; +---------+------+----------------------------------------------------+
| Level | Code | Message |
+---------+------+----------------------------------------------------+
| Warning | 1264 | Out of range value for column 'Fpay_time' at row 1 |
| Warning | 1264 | Out of range value for column 'Fend_time' at row 1 |
+---------+------+----------------------------------------------------+
2 rows in set (0.00 sec) MySQL [loleina]> select * from t_payfund_log_20181121 \g +-------------------------+----------------+---------+--------+--------------------+--------------------+----------------------------+---------------------+
| Fbank_seq | Flast_bank_seq | Fstatus | Fuid | Flast_interface_id | Fmemo | Fpay_time | Fend_time |
+-------------------------+----------------+---------+--------+--------------------+--------------------+----------------------------+---------------------+
| 23020190915090211222001 | 2 | 2 | 540977 | 998501 | test1 &pay failed | NULL | NULL |
| 23020190915090211222002 | 2 | 3 | 34566 | 99802 | 0 | NULL | NULL |
| 23020190915090211222003 | 3 | 2 | 44322 | 997601 | test3 | NULL | NULL |
| 23020190915090211222006 | NULL | 0 | 540977 | 998501 | NULL | 0000-00-00 00:00:00.000000 | 0000-00-00 00:00:00 |
| 23020190915090211222008 | NULL | 0 | 540977 | 998501 | NULL | 0000-00-00 00:00:00.000000 | 0000-00-00 00:00:00 |
+-------------------------+----------------+---------+--------+--------------------+--------------------+----------------------------+---------------------+
5 rows in set (0.00 sec)
C: 嚴格模式下設置sql_mode =‘NO_ZERO_DATE
’,輸入日期=‘0000-00-00'’ 插入直接報錯,輸入‘000-00-00’則不報錯
MySQL [loleina]> SELECT @@GLOBAL.sql_mode; +----------------------------------+
| @@GLOBAL.sql_mode |
+----------------------------------+
| STRICT_TRANS_TABLES,NO_ZERO_DATE |
+----------------------------------+
1 row in set (0.00 sec) MySQL [loleina]> INSERT INTO `loleina`.`t_payfund_log_20181121` (`Fbank_seq`, `Fuid`, `Flast_interface_id`, `Fpay_time`, `Fend_time`) VALUES ('23020190915090211222009', '540977', '998501', 0000-00-00, 0000-00-00); ERROR 1292 (22007): Incorrect datetime value: '0' for column 'Fpay_time' at row 1
INSERT INTO `loleina`.`t_payfund_log_20181121` (`Fbank_seq`, `Fuid`, `Flast_interface_id`, `Fpay_time`, `Fend_time`) VALUES ('23020190915090211222009', '540977', '998501', 1000-00-00, '1999-01-01 11:11:11');
Query OK, 1 row affected (0.00 sec)
類型六:NO_ZERO_IN_DATE
NO_ZERO_IN_DATE 認為日期 '0000-00-00' 是否非法,與是否設置后面的嚴格模式有關。 在嚴格模式,只有‘0000-00-00’能正常插入,輸入‘1000-00-00’報錯; 在非嚴格模式,可以接受‘0000-00-00’,但會生成警告。NO_ZERO_IN_DATE :MySQL中插入的時間字段值,支持‘0000-00-00’插入, 但是只要日期的月和日中含有0值就跟嚴格模式設置有關。 INSERT INTO `loleina`.`t_payfund_log_20181121` (`Fbank_seq`, `Fuid`, `Flast_interface_id`, `Fpay_time`, `Fend_time`) VALUES ('23020190915090211222010', '540977', '998501', 0000-00-00, 0000-00-00);
A: 非嚴格模式設置sql_mode =‘NO_ZERO_IN_DATE ’,支持日期=‘0000-00-00',輸入日期=‘1000-00-00'則告警
MySQL [loleina]> SELECT @@GLOBAL.sql_mode;
+-------------------+
| @@GLOBAL.sql_mode |
+-------------------+
| NO_ZERO_IN_DATE |
+-------------------+
1 row in set (0.00 sec)
MySQL [loleina]> INSERT INTO `loleina`.`t_payfund_log_20181121` (`Fbank_seq`, `Fuid`, `Flast_interface_id`, `Fpay_time`, `Fend_time`) VALUES ('23020190915090211222010', '540977', '998501', 0000-00-00, 0000-00-00);
Query OK, 1 row affected (0.00 sec)
MySQL [loleina]> INSERT INTO `loleina`.`t_payfund_log_20181121` (`Fbank_seq`, `Fuid`, `Flast_interface_id`, `Fpay_time`, `Fend_time`) VALUES ('23020190915090211222011', '540977', '998501', 1000-00-00, 0000-00-00);
Query OK, 1 row affected, 1 warning (0.00 sec)
MySQL [loleina]> show warnings;
+---------+------+------------------------------------------------+
| Level | Code | Message |
+---------+------+------------------------------------------------+
| Warning | 1265 | Data truncated for column 'Fpay_time' at row 1 |
+---------+------+------------------------------------------------+
1 row in set (0.00 sec)
MySQL [loleina]> SELECT * FROM t_payfund_log_20181121 WHERE Fbank_seq = '23020190915090211222012' \g
+-------------------------+----------------+---------+--------+--------------------+-------+----------------------------+---------------------+
| Fbank_seq | Flast_bank_seq | Fstatus | Fuid | Flast_interface_id | Fmemo | Fpay_time | Fend_time |
+-------------------------+----------------+---------+--------+--------------------+-------+----------------------------+---------------------+
| 23020190915090211222012 | NULL | 0 | 540977 | 998501 | NULL | 0000-00-00 00:00:00.000000 | 0000-00-00 00:00:00 |
+-------------------------+----------------+---------+--------+--------------------+-------+----------------------------+---------------------+
1 row in set (0.00 sec)
B: 嚴格模式下設置sql_mode =‘NO_ZERO_IN_DATE ’,輸入日期=‘0000-00-00'’告警,輸入‘1000-00-00’則直接報錯
MySQL [loleina]> SELECT @@GLOBAL.sql_mode; +-------------------------------------+
| @@GLOBAL.sql_mode |
+-------------------------------------+
| STRICT_TRANS_TABLES,NO_ZERO_IN_DATE |
+-------------------------------------+
1 row in set (0.00 sec) MySQL [loleina]> INSERT INTO `loleina`.`t_payfund_log_20181121` (`Fbank_seq`, `Fuid`, `Flast_interface_id`, `Fpay_time`, `Fend_time`) VALUES ('23020190915090211222013', '540977', '998501', 0000-00-00, 0000-00-00); Query OK, 1 row affected (0.00 sec) MySQL [loleina]> SELECT * FROM t_payfund_log_20181121 WHERE Fbank_seq = '23020190915090211222013' \g +-------------------------+----------------+---------+--------+--------------------+-------+----------------------------+---------------------+
| Fbank_seq | Flast_bank_seq | Fstatus | Fuid | Flast_interface_id | Fmemo | Fpay_time | Fend_time |
+-------------------------+----------------+---------+--------+--------------------+-------+----------------------------+---------------------+
| 23020190915090211222013 | NULL | 0 | 540977 | 998501 | NULL | 0000-00-00 00:00:00.000000 | 0000-00-00 00:00:00 |
+-------------------------+----------------+---------+--------+--------------------+-------+----------------------------+---------------------+
1 row in set (0.00 sec) MySQL [loleina]> INSERT INTO `loleina`.`t_payfund_log_20181121` (`Fbank_seq`, `Fuid`, `Flast_interface_id`, `Fpay_time`, `Fend_time`) VALUES ('23020190915090211222013', '540977', '998501', 1000-00-00, 0000-00-00); ERROR 1292 (22007): Incorrect datetime value: '1000' for column 'Fpay_time' at row 1
類型七:STRICT_TRANS_TABLES
嚴格模式,STRICT_TRANS_TABLES
不是幾種策略的組合,單獨指 INSERT
、UPDATE
出現少值或無效值該如何處理,考慮以下5種情況:
1、整型字段數據超長,DB設置Fstatus長度為6,實際插入數據為12345678
2、字符串字段數據超長,DB設置Fmemo長度為25,實際插入數據為30.FmemoFmemoFmemoFmemoFmemoFmemo
3、設置Fstatu的默認值為0,插入時帶這個字段,數據為''
4、設置Fstatu的默認值為空,插入時不帶這個字段
5、Fststus字段為int類型,但是插入string類型的數據:abc
A: sql_mode 為空:報error錯誤:
1、整型字段長度超長溢出,mysql自動校正為類型最長值插入DB,風險很高
MySQL [loleina]> SELECT @@GLOBAL.sql_mode; +-------------------+ | @@GLOBAL.sql_mode | +-------------------+ | | +-------------------+ 1 row in set (0.01 sec) MySQL [loleina]> INSERT INTO `loleina`.`t_payfund_log_20181121` (`Fbank_seq`, `Flast_bank_seq`, `Fstatus`, `Fuid`, `Flast_interface_id`, `Fmemo`, `Fpay_time`) VALUES ('23020190917090211222001', '3', '12345678', '44322', '997601', 'Fmemo', NULL); Query OK, 1 row affected, 1 warning (0.00 sec) MySQL [loleina]> show warnings; +---------+------+--------------------------------------------------+ | Level | Code | Message | +---------+------+--------------------------------------------------+ | Warning | 1264 | Out of range value for column 'Fstatus' at row 1 | +---------+------+--------------------------------------------------+ 1 row in set (0.00 sec) MySQL [loleina]> select * from t_payfund_log_20181121 where Fbank_seq='23020190917090211222001' \G *************************** 1. row *************************** Fbank_seq: 23020190917090211222001 Flast_bank_seq: 3 Fstatus: 32767 Fuid: 44322 Flast_interface_id: 997601 Fmemo: Fmemo Fpay_time: NULL 1 row in set (0.00 sec)
2、字符串字段數據超長被截斷保存到DB
MySQL [loleina]> INSERT INTO `loleina`.`t_payfund_log_20181121` (`Fbank_seq`, `Flast_bank_seq`, `Fstatus`, `Fuid`, `Flast_interface_id`, `Fmemo`, `Fpay_time`) VALUES ('23020190917090211222002', '3', '2', '44322', '997601', 'FmemoFmemoFmemoFmemoFmemoFmemo', NULL); Query OK, 1 row affected, 1 warning (0.00 sec) MySQL [loleina]> show warnings; +---------+------+--------------------------------------------+ | Level | Code | Message | +---------+------+--------------------------------------------+ | Warning | 1265 | Data truncated for column 'Fmemo' at row 1 | +---------+------+--------------------------------------------+ 1 row in set (0.00 sec) MySQL [loleina]> select * from t_payfund_log_20181121 where Fbank_seq='23020190917090211222002' \G *************************** 1. row *************************** Fbank_seq: 23020190917090211222002 Flast_bank_seq: 3 Fstatus: 2 Fuid: 44322 Flast_interface_id: 997601 Fmemo: FmemoFmemoFmemoFmemo Fpay_time: NULL 1 row in set (0.00 sec)
3、設置Fstatu的默認值為0,插入時帶這個字段,但數據為'',實際插入為0
MySQL [loleina]> INSERT INTO `loleina`.`t_payfund_log_20181121` (`Fbank_seq`, `Flast_bank_seq`, `Fstatus`, `Fuid`, `Flast_interface_id`, `Fmemo`, `Fpay_time`) VALUES ('23020190917090211222002', '3', '2', '44322', '997601', 'FmemoFmemoFmemoFmemoFmemoFmemo', NULL); Query OK, 1 row affected, 1 warning (0.00 sec) MySQL [loleina]> show warnings; +---------+------+--------------------------------------------+ | Level | Code | Message | +---------+------+--------------------------------------------+ | Warning | 1265 | Data truncated for column 'Fmemo' at row 1 | +---------+------+--------------------------------------------+ 1 row in set (0.00 sec) MySQL [loleina]> select * from t_payfund_log_20181121 where Fbank_seq='23020190917090211222002' \G *************************** 1. row *************************** Fbank_seq: 23020190917090211222002 Flast_bank_seq: 3 Fstatus: 2 Fuid: 44322 Flast_interface_id: 997601 Fmemo: FmemoFmemoFmemoFmemo Fpay_time: NULL 1 row in set (0.00 sec)
4、設置Fstatu的默認值為空,插入時不帶這個字段,實際插入0
MySQL [loleina]> INSERT INTO `loleina`.`t_payfund_log_20181121` (`Fbank_seq`, `Flast_bank_seq`, `Fuid`, `Flast_interface_id`, `Fmemo`) VALUES ('23020190917090211222004', '3', '44322', '997601', 'Fmemo');
Query OK, 1 row affected, 1 warning (0.01 sec)
MySQL [loleina]> show warnings;
+---------+------+----------------------------------------------+
| Level | Code | Message |
+---------+------+----------------------------------------------+
| Warning | 1364 | Field 'Fstatus' doesn't have a default value |
+---------+------+----------------------------------------------+
1 row in set (0.00 sec)
MySQL [loleina]> select * from t_payfund_log_20181121 where Fbank_seq='23020190917090211222004' \G
*************************** 1. row ***************************
Fbank_seq: 23020190917090211222004
Flast_bank_seq: 3
Fstatus: 0
Fuid: 44322
Flast_interface_id: 997601
Fmemo: Fmemo
Fpay_time: NULL
1 row in set (0.00 sec)
5、Fststus字段為int類型,但是插入string類型的數據:abc,實際插入為0
MySQL [loleina]> INSERT INTO `loleina`.`t_payfund_log_20181121` (`Fbank_seq`, `Flast_bank_seq`, `Fstatus`, `Fuid`, `Flast_interface_id`, `Fmemo`, `Fpay_time`) VALUES ('23020190916090211222005', '3', 'ABC', '44322', '997601', 'Fmemo', NULL); Query OK, 1 row affected, 1 warning (0.00 sec) MySQL [loleina]> show warnings; +---------+------+--------------------------------------------------------------+ | Level | Code | Message | +---------+------+--------------------------------------------------------------+ | Warning | 1366 | Incorrect integer value: 'ABC' for column 'Fstatus' at row 1 | +---------+------+--------------------------------------------------------------+ 1 row in set (0.00 sec) MySQL [loleina]> select * from t_payfund_log_20181121 where Fbank_seq='23020190917090211222005' \G Empty set (0.00 sec) MySQL [loleina]> select * from t_payfund_log_20181121 where Fbank_seq='23020190916090211222005' \G *************************** 1. row *************************** Fbank_seq: 23020190916090211222005 Flast_bank_seq: 3 Fstatus: 0 Fuid: 44322 Flast_interface_id: 997601 Fmemo: Fmemo Fpay_time: NULL 1 row in set (0.00 sec)
B : sql_mode =‘STRICT_TRANS_TABLES ’設置為嚴格模式,1-5插入數據全失敗,報error錯誤:
MySQL [loleina]> SELECT @@GLOBAL.sql_mode; +---------------------+ | @@GLOBAL.sql_mode | +---------------------+ | STRICT_TRANS_TABLES | +---------------------+ 1 row in set (0.00 sec) 整型超長 MySQL [loleina]> INSERT INTO `loleina`.`t_payfund_log_20181121` (`Fbank_seq`, `Flast_bank_seq`, `Fstatus`, `Fuid`, `Flast_interface_id`, `Fmemo`, `Fpay_time`) VALUES ('23020190916090211222001', '3', '12345678', '44322', '997601', 'Fmemo', NULL); ERROR 1264 (22003): Out of range value for column 'Fstatus' at row 1 字符串超長 MySQL [loleina]> INSERT INTO `loleina`.`t_payfund_log_20181121` (`Fbank_seq`, `Flast_bank_seq`, `Fstatus`, `Fuid`, `Flast_interface_id`, `Fmemo`, `Fpay_time`) VALUES ('23020190916090211222001', '3', '2', '44322', '997601', 'FmemoFmemoFmemoFmemoFmemoFmemo', NULL); ERROR 1406 (22001): Data too long for column 'Fmemo' at row 1 設置Fstatu的默認值為0,插入時帶這個字段,但是設置為'' MySQL [loleina]> INSERT INTO `loleina`.`t_payfund_log_20181121` (`Fbank_seq`, `Flast_bank_seq`, `Fstatus`, `Fuid`, `Flast_interface_id`, `Fmemo`, `Fpay_time`) VALUES ('23020190916090211222001', '3', '', '44322', '997601', Fmemo, NULL); ERROR 1366 (HY000): Incorrect integer value: '' for column 'Fstatus' at row 1 設置Fstatu的默認值為'' MySQL [loleina]> INSERT INTO `loleina`.`t_payfund_log_20181121` (`Fbank_seq`, `Flast_bank_seq`, `Fuid`, `Flast_interface_id`, `Fmemo`) VALUES ('23020190916090211222004', '3', '44322', '997601', 'Fmemo'); ERROR 1364 (HY000): Field 'Fstatus' doesn't have a default value Fststus為int,但是插入string類型 MySQL [loleina]> INSERT INTO `loleina`.`t_payfund_log_20181121` (`Fbank_seq`, `Flast_bank_seq`, `Fstatus`, `Fuid`, `Flast_interface_id`, `Fmemo`, `Fpay_time`) VALUES ('23020190916090211222005', '3', 'ABC', '44322', '997601', 'Fmemo', NULL); ERROR 1366 (HY000): Incorrect integer value: 'ABC' for column 'Fstatus'
2.3、三個重要且常用的sql_mode模式
2.3.1ANSI
此模式下,更接近標准sql。包含REAL_AS_FLOAT
,PIPES_AS_CONCAT
,ANSI_QUOTES
,IGNORE_SPACE
,ONLY_FULL_GROUP_BY
.
寬松模式,對插入數據進行校驗,如果不符合定義類型或長度,會對數據類型調整或截斷保存,報warning警告。
2.3.2 STRICT_TRANS_TABLES
該選項針對事務性存儲引擎生效,對於非事務性存儲引擎無效,該選項表示開啟strict sql模式。在strict sql模式下,在INSERT或者UPDATE語句中,插入或者更新了某個不符合規定的字段值,則會直接報錯中斷操作。包含: ERROR_FOR_DIVISION_BY_ZERO
, NO_ZERO_DATE
, and NO_ZERO_IN_DATE
modes. 從mysql 5.7.5開始,默認的sql模式包括此模式。
嚴格模式,進行數據的嚴格校驗,錯誤數據不能插入,報error錯誤。
2.3.3 TRADITIONAL
嚴格模式,當向mysql數據庫插入數據時,進行數據的嚴格校驗,保證錯誤數據不能插入,報error錯誤。用於事務時,會進行事物的回滾。
三:測試策略
3.1 小結:sql_mode=''和其他模式下影響的sql以及結果對比
3.2 寬松模式下測試應該注意哪些地方?
1、怎么查看出現的warings?
使用show warnings查看最新warings日志,但在多客戶端使用的情況下,這個日志很快就會被刷新而捕捉不到。 在非嚴格模式下,一些mysql語句會報error錯,這類error錯誤不會記日志,使用show warnings可以看見。show warnings是一個診斷語句,它顯示有關在當前會話中最新的執行語句所導致的條件(錯誤、警告和注釋)的信息。
2、可以把現網的sql_mode更改為嚴格模式來防止一些問題的產生嗎?
實際一般現網運營的DB設置的模式都是嚴格模式,連mysql的5.7以上的版本默認都是嚴格模式,但是可能有些系統運行很久了,在mysql進行升級的時候,可以把sql_mode從原來的寬松模式更改為嚴格模式嗎?
現網是不可以的,mysql官網明確指出:“在創建好表和插入數據到分區表中之后,更改服務器SQL模式可能會導致此類表的行為發生重大變化,並可能導致數據丟失或損壞。強烈建議在使用用戶定義的分區創建表后,不
要更改sql模式。當復制分區表時,主和從機上不同的SQL模式也會導致問題。為了獲得最佳結果,您應該始終在主服務器和從服務器上使用相同的服務器sql模式。”
3、寬松模式下使用mysql數據庫測試需要注意什么呢?
A:注意表的字段的長度和數據類型設計是否合理。因為在長度設計過短或者數據類型設計錯誤時,寬松模式下插入或者更新是不會直接報error錯誤的。
B: 檢查每一筆數據插入時,字段值的正確性。