MySQL 在比較運算子兩側資料型別不一致時,會自動發生隱式轉換。這不僅可能導致查詢結果違反直覺,更是導致索引失效、查詢變慢的常見元兇。本文透過實測數據,深度剖析數值型與字串型欄位的轉換行為差異。
CREATE TABLE `emp` (
`EMPNO` INT(11) NOT NULL PRIMARY KEY,
`ENAME` VARCHAR(15) NOT NULL,
`JOB` VARCHAR(15) NOT NULL,
`MGR` INT(11) DEFAULT '0',
`HIREDATE` TIMESTAMP NULL DEFAULT NULL,
`SAL` INT(20) DEFAULT '0',
`COMM` INT(11) DEFAULT '0',
`DEPTNO` INT(11) NOT NULL,
KEY `idx_ename` (`ENAME`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 關鍵測試資料:ENAME 包含純數字 '23456'
INSERT INTO `emp` VALUES (7936, '23456', 'BOSCO-DBA', 7788, '2014-09-13 16:13:56', 2450, 800, 10);
-- 查詢 1:數值比數值(基準)
EXPLAIN SELECT * FROM `emp` WHERE `EMPNO` = 7788;
-- type=const, key=PRIMARY, rows=1 ✅ 索引生效
-- 查詢 2:數值比字串 '7788'
EXPLAIN SELECT * FROM `emp` WHERE `EMPNO` = '7788';
-- type=const, key=PRIMARY, rows=1 ✅ 索引生效,無隱式轉換影響
-- 查詢 3:字串含非數字尾碼 '7788ab12'
SELECT * FROM `emp` WHERE `EMPNO` = '7788ab12';
-- 結果:命中 EMPNO=7788 的行!
-- Warning: Truncated incorrect DOUBLE value: '7788ab12'
-- 解析:MySQL 將字串轉 DOUBLE,取前導數字 7788,索引仍生效
-- 查詢 4:字串非數字開頭 'ab7788'
SELECT * FROM `emp` WHERE `EMPNO` = 'ab7788';
-- 結果:Empty set(等同 WHERE EMPNO = 0)
結論: 數值型欄位比較字串常數時,MySQL 將字串轉為數值,**不影響索引使用**。但會產生 Warning,且語意模糊,生產環境**嚴禁**此類寫法。
-- 查詢 1:字串比字串(基準)
EXPLAIN SELECT * FROM `emp` WHERE `ENAME` = '23456';
-- type=ref, key=idx_ename, rows=1 ✅ 索引生效
-- 查詢 2:字串比數值 23456
EXPLAIN SELECT * FROM `emp` WHERE `ENAME` = 23456;
-- type=ALL, key=NULL, rows=15 (全表掃描) ❌ 索引失效!
-- 14 warnings: Truncated incorrect DOUBLE value: 'SMITH', 'ALLEN'...
-- 為什麼?MySQL 將 ENAME 欄位每一行值轉為數值再比較,等同:
-- WHERE CAST(`ENAME` AS UNSIGNED) = 23456
-- 函數作用在欄位上 → 索引失效 → 全表掃描
| 欄位型別 | 輸入型別 | 轉換方向 | 索引影響 | 根本原因 |
|---|---|---|---|---|
| 數值 (INT) | 字串 | 字串 → 數值 | ✅ 生效 | 常數折疊,轉換發生在常數側 |
| 字串 (VARCHAR) | 數值 | 欄位值 → 數值 | ❌ 失效 | 函數作用在欄位側,導致索引無法使用 |
| 字串 (VARCHAR) | 字串 | 無 | ✅ 生效 | 型別一致,無轉換 |
? 佔位符),驅動程式會依欄位型別自動轉換參數,杜絕拼接 SQL 字串。Integer 對應 INT,String 對應 VARCHAR)。WHERE varchar_col = 123、WHERE int_col = 'abc' 模式。log_warnings=2 或監控 Truncated incorrect DOUBLE value 警告計數。EXPLAIN,確認 type 非 ALL、key 非 NULL。DATE/DATETIME 比較字串:WHERE create_time = '2024-05-20' 安全;但 WHERE DATE(create_time) = '2024-05-20' 會導致索引失效(函數在欄位上)。DECIMAL 比較整數/浮點數:可能發生精度截斷,建議統一用字串字面值 '123.45'。ENUM/SET 比較數值:WHERE status = 1 會按索引位置匹配,非標籤值,極易寫錯,務必用字串 'active'。