表 indextest(id PK, name VARCHAR(10) INDEX, age TINYINT INDEX, create_time TIMESTAMP INDEX)。
查詢 WHERE name = '111222'(字符串)走索引 idx_name;
查詢 WHERE name = 111222(數字)**全表掃描**,索引失效。
當比較運算子兩側類型不一致時,MySQL 按以下優先級進行**隱式轉換**(參考文檔 Type Conversion in Expression Evaluation):
NULL → 結果 NULL(<=> 除外)。TIMESTAMP/DATETIME,另為常量 → 常量轉 TIMESTAMP。DECIMAL:另為 DECIMAL/整數 → 轉 DECIMAL;另為浮點數 → 轉浮點數。VARCHAR = 數字 會走全表掃描?屬於第 7 類:**列值(字符串)與參數(數字)雙雙轉為浮點數比較**。
'111222'、'111222abc'、' 111222'、'11122.2e1' 等**無限多字符串**的浮點數值均等於 111222。數字列 = 字符串 仍能走索引EXPLAIN SELECT name FROM indextest WHERE age = '30';
-- type=ref, key=idx_age, rows=1
原因:MySQL 將字符串 '30' **常量折疊**為確定整數 30,再在索引上精確定位,**列值無需轉換**,索引有效。
| 場景 | SQL 示例 | 失效原因 | 修正 |
|---|---|---|---|
| 函數作用於索引列 | WHERE CAST(age AS UNSIGNED)=30 | 列值被轉換,索引順序破壞 | WHERE age=30 |
| 日期列運算 | WHERE DATE(create_time)='2012-02-02' | DATE() 函數破壞索引有序性 | WHERE create_time >= '2012-02-02' AND create_time < '2012-02-03' |
| 字符集/校對集不匹配 | WHERE utf8_col = latin1_param | 隱式字符集轉換 | 統一字符集、顯式 CONVERT() |
| 數字精度溢出 | '18015376320243459' = 18015376320243459 → false | 雙方轉浮點數,53 位尾數精度不足 | CAST('...' AS UNSIGNED) = 18015376320243459 |
VARCHAR 傳字符串,INT 傳整數。WHERE name = '111222' 而非 WHERE name = 111222。CAST 而非依賴隱式轉換:WHERE CAST(col AS UNSIGNED) = 123 明確語義,雖同樣失效索引,但語義清晰、可被靜態分析工具識別。sql_mode=STRICT_ALL_TABLES,NO_ENGINE_SUBSTITUTION:嚴格模式下部分隱式轉換會報警/報錯。EXPLAIN SELECT * FROM indextest WHERE name = 111222;
-- 觀察 type=ALL, key=NULL
SET SESSION optimizer_trace='enabled=on';
SELECT * FROM indextest WHERE name = 111222;
SELECT * FROM information_schema.optimizer_trace;
-- 可見 "condition_processing" 階段的 "constant_propagation" 與 "type_conversion" 細節
MySQL 隱式類型轉換是**索引失效的隱形殺手**,尤其「字符串列比較數字」觸發雙向浮點數轉換,徹底摧毀 B+Tree 有序性。強制應用層類型一致、SQL 常量加引號、避免索引列函數運算、善用 EXPLAIN 與 Optimizer Trace 驗證,是每位開發者、DBA 的必修功課。記住:**索引列原樣參與比較,才是索引生效的前提**。