談談MySQL中的隱式轉換
工作過程中會遇到比較多關于隱式轉換的案例,隱式轉換除了會導致慢查詢,還會導致數據不準。本文通過幾個生產中遇到的案例來。
基礎知識
關于比較運算的原則,MySQL官方文檔的描述: https://dev.mysql.com/doc/refman/5.6/en/type-conversion.html
如果 判斷符號左右兩邊有一個為NULL,結果就是null,除非使用安全的等值判斷 <=>
(none) 05:17:16 >select null = null;+-------------+| null = null |+-------------+|NULL |+-------------+1 row in set (0.00 sec)(none) 05:34:59 >select null <=> null;+---------------+| null <=> null |+---------------+| 1 |+---------------+1 row in set (0.00 sec)(none) 05:35:51 >select null != 1;+-----------+| null != 1 |+-----------+| NULL |+-----------+1 row in set (0.00 sec)
如何判斷左右兩邊都是相同類型的,比如都是字符串,則以字符串進行對比。如果是數字,則以數字進行比較。
注意 對于比較常見的 字符串與數字類型的比較的情況,如果字符串字段是索引字段,那么MySQL 無法通過索引進行查找數據,比如以下例子:
(none) 05:39:42 >select 1=’1’;+-------+| 1=’1’ |+-------+| 1 |+-------+1 row in set (0.00 sec)(none) 05:39:44 >select 1=’1A’;+--------+| 1=’1A’ |+--------+| 1 |+--------+1 row in set, 1 warning (0.00 sec)(none) 05:39:47 >select 1=’1 ’; ##1后有空格+--------+| 1=’1 ’ |+--------+| 1 |+--------+1 row in set (0.00 sec)
MySQL 認為數字1 與’1’,’1_’,’1A’ 相等,故無法通過索引二分查找準確定位到具體的值。
Hexadecimal(十六進制)以二進制字符串的方式進行比較。
如何判斷符號左邊是 timestamp 或者datetime類型的,右邊是常量,在比較之前,常量會被轉換為時間類型。
隱式轉換
字段類型不一樣
In all other cases, the arguments are compared as floating-point (real) numbers.
除了以上的其他類型的比較,系統將字段和參數轉換為浮點型進行比較。使用浮點數(或轉換為浮點數的值)的比較是近似的,因為這樣的數字是不精確的??聪旅?個例子
>select ’190325171202362933’ = 190325171202362931;+-------------------------------------------+| ’190325171202362933’ = 190325171202362931 |+-------------------------------------------+| 1 |+-------------------------------------------+1 row in set (0.00 sec)>select ’190325171202362936’ = 190325171202362931;+-------------------------------------------+| ’190325171202362936’ = 190325171202362931 |+-------------------------------------------+| 1 |+-------------------------------------------+1 row in set (0.00 sec)
直觀上不相等的值,做等值判斷之后竟然返回為1。這樣帶來2個問題不能利用索引且結果數據不準
>select ’190325171202362931’+0.0;+--------------------------+| ’190325171202362931’+0.0 |+--------------------------+| 1.9032517120236294e17 |+--------------------------+1 row in set (0.00 sec)>select ’190325171202362936’+0.0;+--------------------------+| ’190325171202362936’+0.0 |+--------------------------+| 1.9032517120236294e17 |+--------------------------+1 row in set (0.00 sec)
將上面的值轉換為浮點數,都是 1.9032517120236294e17,所以判斷相等時為真,返回True。
in 參數包含多個類型
具體的案例參考之前的一篇文章MySQL優化案例一則 ,where 條件 in 集合里面的數據類型不一樣,執行計劃未利用到索引
淘寶MySQL月報(http://mysql.taobao.org/monthly/2017/12/06/ )里面有一篇正好和這個一樣的案例,推薦給大家 簡單說,就是在IN的入口有一個判斷, 如果in中的字段類型不兼容, 則認為不可使用索引.
而這個arg_types_compatible 的賦值邏輯是:
if (type_cnt == 1) arg_types_compatible = TRUE;
也就是說,當IN列表中出現超過一個字段類型時, 就認為類型不兼容,從而不能利用索引。
字符集類型不一致
環境準備:
CREATE TABLE `t1` (`id` int(11) NOT NULL AUTO_INCREMENT,`c1` varchar(20) DEFAULT NULL,`c2` varchar(50) DEFAULT NULL,PRIMARY KEY (`id`),KEY `idx_c1` (`c1`),KEY `idx_c2` (`c2`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;CREATE TABLE `t2` (`id` int(11) NOT NULL AUTO_INCREMENT,`c1` varchar(20) DEFAULT NULL,`c2` varchar(50) DEFAULT NULL,PRIMARY KEY (`id`),KEY `idx_c1` (`c1`),KEY `idx_c2` (`c2`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;insert into t1(c1,c2) values(’a’,’a’),(’b’,’b’),(’c’,’c’),(’d’,’d’),(’e’,’e’);insert into t2(c1,c2) values(’a’,’a’),(’b’,’b’),(’c’,’c’),(’d’,’d’),(’e’,’e’);
測試結果
小結
希望通過以上案例,基礎知識介紹,開發同學能少走彎路,在開發編寫sql的階段一定要明確字段的類型,尤其是看起來像數字類型的id,xxxid,xxxno 這類字段,實際上可能是字符類型。
以上就是談談MySQL中的隱式轉換的詳細內容,更多關于MySQL 隱式轉換的資料請關注好吧啦網其它相關文章!
相關文章: