1. 程式人生 > 資料庫 >談談MySQL中的隱式轉換

談談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中的隱式轉換

淘寶MySQL月報(http://mysql.taobao.org/monthly/2017/12/06/ )裡面有一篇正好和這個一樣的案例,推薦給大家簡單說,就是在IN的入口有一個判斷,如果in中的欄位型別不相容,則認為不可使用索引.

談談MySQL中的隱式轉換

而這個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');

測試結果

談談MySQL中的隱式轉換

小結

希望通過以上案例,基礎知識介紹,開發同學能少走彎路,在開發編寫sql的階段一定要明確欄位的型別,尤其是看起來像數字型別的id,xxxid,xxxno 這類欄位,實際上可能是字元型別。

以上就是談談MySQL中的隱式轉換的詳細內容,更多關於MySQL 隱式轉換的資料請關注我們其它相關文章!