











在数据库优化中,我们经常会关注索引的创建、SQL的写法,但有一个隐蔽的“性能杀手”却容易被忽略——字段数据类型与查询值类型不匹配。
我们有一张交易表 plat_order,其中 order_id 字段定义为 VARCHAR(20),并且在该字段上建立了索引 index_order_id。
当执行以下查询时:
-- 查询A:使用数字字面量
SELECT * FROM plat_order WHERE order_id = 1669233832303042562;
这条看似简单的查询却不会走索引,而是进行了全表扫描,执行时间可能长达数秒甚至更久。
而当我们稍作修改:
-- 查询B:使用字符串字面量
SELECT * FROM plat_order WHERE order_id = '1669233832303042562';
同样的查询条件,只是将数字改为字符串,执行时间立即降至毫秒级。
通过 EXPLAIN 分析两条SQL的执行计划,差异一目了然:
| 查询语句 | type | key | rows | Extra | 性能评估 |
|---|---|---|---|---|---|
WHERE order_id = 16692... |
ALL | (null) | 8,533,253 | Using where | 全表扫描,极慢 |
WHERE order_id = '16692...' |
ref | index_order_id | 1 | (null) | 索引扫描,极快 |
从执行计划可以清晰看到:
type=ALL 表示全表扫描,key=(null) 表示未使用任何索引,需要处理853万行数据type=ref 表示使用索引等值查找,key=index_order_id 表示使用了该索引,仅需处理1行数据当SQL语句中参与比较运算的两个数据类型不一致时,MySQL会自动将它们转换为相同类型,这个过程称为隐式类型转换。
MySQL遵循一个核心原则:总是尝试将值转换为表达式中另一操作数的类型。具体到数字与字符串的比较:
当字符串列与数字比较时(VARCHAR = 数字):
当数字列与字符串比较时(INT = '字符串'):
这是问题的关键。当执行 WHERE order_id = 1669233832303042562 时:
1669233832303042562 转换为字符串-- MySQL实际执行的近似逻辑(概念上):
SELECT * FROM plat_order
WHERE CONVERT(order_id USING utf8mb4) = CONVERT(1669233832303042562 USING utf8mb4);
-- 注意:order_id上的函数操作导致索引失效
如果 order_id 是数字类型(如 BIGINT),执行 WHERE order_id = '1669233832303042562':
'1669233832303042562' 转换为数字'ABC123'),会转换为0,可能导致错误的结果-- 示例:数字列与字符串比较
CREATE TABLE order_num (
id BIGINT PRIMARY KEY,
order_id BIGINT,
INDEX idx_order_id(order_id)
);
-- 可以走索引(字符串转为数字)
EXPLAIN SELECT * FROM order_num WHERE order_id = '1669233832303042562';
-- type: ref, key: idx_order_id
-- 也可以走索引(直接使用数字)
EXPLAIN SELECT * FROM order_num WHERE order_id = 1669233832303042562;
-- type: ref, key: idx_order_id
| 场景 | 转换方向 | 索引是否可用 | 性能影响 |
|---|---|---|---|
| 字符串列 = 数字值 | 数字→字符串 | 否 | 全表扫描,性能极差 |
| 数字列 = 字符串值 | 字符串→数字 | 是 | 正常使用索引 |
| 字符串列 = 字符串值 | 无转换 | 是 | 最佳性能 |
| 数字列 = 数字值 | 无转换 | 是 | 最佳性能 |
黄金法则:永远让查询值的类型与列定义的类型保持一致。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。