在 MySQL 中執行 WHERE title LIKE '%a%' 時,返回的結果包含不含字母 'a' 的中文記錄。本文揭示底層原因:校對規則導致中文字符與某些 ASCII 字符視為「相等」,並提供三種遞進解決方案及性能權衡建議。
-- 數據表 news,title 列含中文、英文混合
SELECT id, title FROM news WHERE title LIKE '%a%';
-- 結果:某些純中文標題(如「新聞資訊」)也被返回,實際不含 'a'
核心原因:校對規則的字符等價類
latin1 或 utf8 配合校對規則 _general_ci(不區分大小寫)。驗證方法:
SELECT '中文' = 'a' COLLATE utf8_general_ci; -- 返回 1 (true) 某些字符
SHOW COLLATION WHERE Charset = 'utf8'; -- 查看所有校對規則
SELECT id, title FROM news WHERE BINARY title LIKE '%a%';
BINARY 強制列按字節值比較,忽略校對規則,區分大小寫。SELECT id, title FROM news
WHERE BINARY UCASE(title) LIKE CONCAT('%', UCASE('a'), '%');
-- 或
SELECT id, title FROM news
WHERE BINARY UCASE(title) LIKE UCASE('%a%');
CONCAT 便於參數化傳入模式無需手動加 %。SELECT id, title FROM news
WHERE title LIKE '%a%' COLLATE utf8_bin;
-- 或建表/修改列時指定
ALTER TABLE news MODIFY title VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_bin;
utf8_bin 按字節二進制比較,不區分大小寫?實際 _bin 校對**區分大小寫**且**精確匹配字節**,中文不會誤匹配英文。| 校對規則 | 大小寫敏感 | 中文誤匹配英文 | 可用索引 | 適用場景 |
|---|---|---|---|---|
utf8_general_ci | 否 | **是** | 是 | 通用業務,接受誤匹配風險 |
utf8_bin | 是 | 否 | 是 | 精確匹配、郵箱、代碼、標識符 |
utf8_unicode_ci | 否 | 較少(更嚴格 Unicode 排序) | 是 | 多語言排序正確性優先 |
BINARY / _bin + 函數 | 是/可控 | 否 | **否** | 臨時查詢、無索引需求 |
-- 100萬行測試表
EXPLAIN SELECT * FROM news WHERE title LIKE '%a%'; -- type=ALL,但可用索引下推 (ICP)
EXPLAIN SELECT * FROM news WHERE BINARY title LIKE '%a%'; -- type=ALL,無索引
EXPLAIN SELECT * FROM news WHERE title LIKE '%a%' COLLATE utf8_bin; -- type=range,可用索引!
結論: 方案 3(列級或查詢級指定 utf8_bin)兼具**精確性**與**索引可用性**,為生產環境首選。方案 1/2 適合臨時查詢、小表、無索引場景。
COLLATE utf8_bin 或 utf8mb4_bin。WHERE col LIKE '%x%' COLLATE utf8_bin。BINARY、UCASE/LOWER、CONCAT 等函數包裹列,導致索引失效。FULLTEXT + ngram 解析器) 或 **Elasticsearch/Solr** 專業搜索引擎,而非 LIKE。