談談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.
除了以上的其他型別的比較,系統將欄位和引數轉換為浮點型進行比較。使用浮點數(或轉換為浮點數的值)的比較是近似的,因為這樣的數字是不精確的。看下面2個例子
>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,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,'e');
測試結果
小結
希望通過以上案例,基礎知識介紹,開發同學能少走彎路,在開發編寫sql的階段一定要明確欄位的型別,尤其是看起來像數字型別的id,xxxid,xxxno 這類欄位,實際上可能是字元型別。
以上就是談談MySQL中的隱式轉換的詳細內容,更多關於MySQL 隱式轉換的資料請關注我們其它相關文章!