關於MySQL自動將varchar型別值的末尾空格不起作用的問題
背景
優化線上系統的一個查詢方法,這裡我舉例說明。
方法的功能是返回所有異常涉及到的容器號,並對其去重。
原來的方法是返回符合要求的Dto物件集合,然後取Dto物件中的containerNo這個屬性,通過map的key來實現去重操作;
現在的方法是在SQL層面只返回container_no欄位,且通過DISTICT 對容器號去重。
SQL內容類似於:
select distinct name from `user` where name = 'zhang';
我們假設資料庫中符合查詢條件的容器號有以下幾個:
ZZW001
ZZW002
-
ZZW001
注意這裡末尾有一個空格 -
ZZW001
那麼新舊方法處理的結果是:
- 舊方法:
ZZW001
ZZW002
ZZW001
(注意這裡末尾有一個空格)ZZW001
(注意這裡末尾假設有三個空格) - 新方法:
ZZW001
ZZW002
之所以這樣,是因為MySQL將字串後面的空格全給過濾了。
我也試了作為where 查詢條件,一樣是無效的,不過字串前面的空格是起作用的,且末尾的空格是可以正常儲存到資料庫裡的,即只有作為條件和資料處理物件時(比如去重),才會“失效”。
查閱了網上的資料,有人這麼說:
如果欄位是char或varchar型別,那麼在字串比較的時候MySQL使用PADSPACE校對規則,會忽略欄位末尾的空格字元。
解決方案:
第一種方法:使用 like
select * from table where user like 'abcdefg ';
第二種方法:使用BINARY
select * from table where user = BINARY 'abcdefg ';
第三種方法:使用length函式
select * from table where user = 'abcdefg ' and length(user) = length('abcdefg ');
注意
如果程式沒有對所有的業務場景做末尾空格的處理,那麼建議後面的查詢不要使用前面的解決方案來處理,否則容易出現問題,如果所有的業務場景在儲存資料的時候,都做了末尾空格的處理,那麼可以查詢的時候可以使用上面的解決方案來實現資料精確查詢。
其實對於資料的去重過濾如果資料不是特別的多,建議使用MySQL處理。以我們線上的這個問題為例,儲存容器號到業務表的時候,可能是使用者在末尾多輸入一個空格,末尾多空格依然可以查出資料,這個時候校驗就通過了,但是存到資料庫的拿使用者輸入的儲存的,即將末尾帶有空格的儲存了,如果後面使用前面的解決方案處理,就會出現問題,比如作為聯表查詢的時候關聯條件時。
但是程式碼的去重都是強校驗,末尾有空格和沒有空格是兩個不同的資料,所以就會出現最開始前面的背景問題。
最終,結合業務的分析,我們認為原來的方法是存在問題的,新方法在資料庫去重是沒問題的。