一、現象
CREATE TABLE `user_info` ( `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主鍵自增ID', `user_name` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '名字', PRIMARY KEY (`id`), KEY `idx_user_name` (`user_name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用戶表'; INSERT INTO user_info(user_name) values('adu'); INSERT INTO user_info(user_name) values('adu '); #一個空格 INSERT INTO user_info(user_name) values('adu '); #四個空格
如上有個user_info表,我們分別插入'adu'、'adu '、'adu '三個用戶。
但我們在查詢時卻遇到一個詭異的現象:
SELECT * FROM adu_user_info WHERE user_name = 'adu'; SELECT * FROM adu_user_info WHERE user_name = 'adu '; #一個空格 SELECT * FROM adu_user_info WHERE user_name = 'adu '; #兩個空格
無論查詢中尾部帶有幾個空格,結果是一樣的,都會命中'adu'、'adu '、'adu '三個用戶。
二、原因
查詢MySQL的官方文檔(https://dev.mysql.com/doc/refman/5.7/en/char.html),原來MySQL的校對規則基於PAD SPACE,這就意味着CHAR、VARCHAR、TEXT等字符串的等值比較(“=”)會忽略掉尾部的空格。
而且這也符合SQL標准,無需設置也無法改變(參考:https://stackoverflow.com/questions/10495692/mysql-comparison-operator-spaces)。
三、想要精確查詢的解決方案
3.1 like
like不會忽略尾部的空格。
SELECT * FROM adu_user_info WHERE user_name LIKE 'adu'; SELECT * FROM adu_user_info WHERE user_name LIKE 'adu '; #一個空格 SELECT * FROM adu_user_info WHERE user_name LIKE 'adu '; #四個空格
3.2 binary
binary不是函數,是類型轉換運算符,它用來強制它后面的字符串為一個二進制字符串,可以理解成精確匹配。
SELECT * FROM adu_user_info WHERE user_name = BINARY 'adu'; SELECT * FROM adu_user_info WHERE user_name = BINARY 'adu '; #一個空格 SELECT * FROM adu_user_info WHERE user_name = BINARY 'adu '; #四個空格
注:這里的BINARY關鍵字要放在“=”的后邊,以便有效利用該字段的索引。
四、結論
- MySQL的CHAR、VARCHAR、TEXT等字符串字段在等值比較("=")時,基於PAD SPACE校對規則,會忽略掉尾部的空格;
- 在存儲時,不會自動截斷尾部的空格,會按原值存儲;
- 如果想要精確查詢就不能用等值查詢(“=”),而應改用like或binary。
五、參考