1. 程式人生 > 實用技巧 >Mysql不容易發現的陷阱--字符集轉換

Mysql不容易發現的陷阱--字符集轉換

本文參考自https://blog.csdn.net/bohu83/article/details/105320327

  假設現在有兩張表

CREATE TABLE `tradelog` (
  `id` int(11) NOT NULL,
  `tradeid` varchar(32) DEFAULT NULL,
  `operator` int(11) DEFAULT NULL,
  `t_modified` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `tradeid` (`tradeid`),
  KEY `t_modified` (`t_modified`)
) ENGINE
=InnoDB DEFAULT CHARSET=utf8mb4;

  

CREATE TABLE `trade_detail` (
  `id` int(11) NOT NULL,
  `tradeid` varchar(32) DEFAULT NULL,
  `trade_step` int(11) DEFAULT NULL, /*操作步驟*/
  `step_info` varchar(32) DEFAULT NULL, /*步驟資訊*/
  PRIMARY KEY (`id`),
  KEY `tradeid` (`tradeid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

  

select d.* from tradelog l, trade_detail d where d.tradeid=l.tradeid and l.id=2;

  • 第1步,是根據id在tradelog表裡找到L2這一行;
  • 第2步,是從L2中取出tradeid欄位的值;
  • 第3步,是根據tradeid值到trade_detail表中查詢條件匹配的行。explain的結果裡面第二行的key=NULL表示的就是,這個過程是通過遍歷主鍵索引的方式,一個一個地判斷tradeid的值是否匹配。

進行到這裡,你會發現第3步不符合我們的預期。因為表trade_detail裡tradeid欄位上是有索引的,我們本來是希望通過使用tradeid索引能夠快速定位到等值的行。但,這裡並沒有。

如果你去問DBA同學,他們可能會告訴你,因為這兩個表的字符集不同,一個是utf8,一個是utf8mb4,所以做表連線查詢的時候用不上關聯欄位的索引。這個回答,也是通常你搜索這個問題時會得到的答案。

但是你應該再追問一下,為什麼字符集不同就用不上索引呢?

我們說問題是出在執行步驟的第3步,如果單獨把這一步改成SQL語句的話,那就是:

mysql> select * from trade_detail where tradeid=$L2.tradeid.value; 

  其中,$L2.tradeid.value的字符集是utf8mb4。

參照前面的兩個例子,你肯定就想到了,字符集utf8mb4是utf8的超集,所以當這兩個型別的字串在做比較的時候,MySQL內部的操作是,先把utf8字串轉成utf8mb4字符集,再做比較。