跳转至

SQL 错误用法汇总

一、WHERE 子句里索引失效

1. 对索引列使用函数

-- ❌ 不走索引
SELECT * FROM t WHERE DATE(create_time) = '2024-01-01';

-- ✅ 改范围
SELECT * FROM t WHERE create_time >= '2024-01-01' AND create_time < '2024-01-02';

2. 对索引列做运算

-- ❌
SELECT * FROM t WHERE age + 1 = 18;

-- ✅
SELECT * FROM t WHERE age = 17;

3. 隐式类型转换

-- ❌ phone 是 varchar,传数字
SELECT * FROM t WHERE phone = 13800001111;

-- ✅
SELECT * FROM t WHERE phone = '13800001111';

4. LIKE 以 % 开头

-- ❌
SELECT * FROM t WHERE name LIKE '%张%';

-- ✅ 前缀能走索引
SELECT * FROM t WHERE name LIKE '张%';

全文检索需求用 ES 或全文索引。

5. OR 连接非索引列

-- ❌ age 无索引,整个查询全表扫
SELECT * FROM t WHERE name = '张三' OR age = 18;

-- ✅ age 也加索引,或用 UNION

6. 联合索引不满足最左前缀

-- 索引 (a, b, c)
-- ❌
SELECT * FROM t WHERE b = 1;
SELECT * FROM t WHERE c = 1;

-- ✅
SELECT * FROM t WHERE a = 1 AND b = 2;

7. != / NOT IN / NOT EXISTS

大多数情况优化器放弃索引,视数据分布而定。

二、SELECT 用法

1. SELECT *

  • 返回不需要的列,浪费 IO。
  • 可能无法用覆盖索引。

2. SELECT COUNT(*) 大表实时统计

count 性能对比

3. 深分页

-- ❌
SELECT * FROM t ORDER BY id LIMIT 1000000, 20;

-- ✅
SELECT * FROM t WHERE id > 1000000 LIMIT 20;

三、JOIN 错误

1. JOIN 字段类型不一致

a.uid = b.uid,一个是 varchar 一个是 bigint,索引失效。

2. 小表不驱动大表

MySQL 优化器通常会选小表驱动,但要注意:被驱动表的 JOIN 字段必须有索引。

3. 过多 JOIN

超过 3~5 张表 JOIN 性能急剧下降,应拆分查询或反范式。

四、GROUP BY / ORDER BY

1. WHERE 后用 HAVING 过滤

-- ❌
SELECT dept, AVG(sal) FROM emp GROUP BY dept HAVING dept = 'IT';

-- ✅
SELECT dept, AVG(sal) FROM emp WHERE dept = 'IT' GROUP BY dept;

2. ORDER BY 字段不在索引里

会 filesort,数据量大时慢。

五、其他

1. 大事务

事务太长,持锁时间久,主从延迟。

2. 循环里执行 SQL

// ❌ N+1
for (Order o : orders) {
    User u = userMapper.selectById(o.getUserId());
}

// ✅ 一次性 IN 查询
List<User> users = userMapper.selectByIds(
    orders.stream().map(Order::getUserId).toList());

3. 大 IN 列表

IN (1, 2, ..., 10000) 性能差,分批或临时表。

一句话

索引失效的本质:优化器无法用 B+ 树有序结构快速定位,只能退化为全表扫描。