索引未被使用或失效是存储过程慢的主因,常见于where子句中对索引字段使用函数(如date(in_date))或隐式类型转换,导致优化器无法利用索引,应改写为范围查询。

加了索引但存储过程还是慢,大概率不是索引没建,而是索引根本没被用上——或者用了,但压根没起到加速作用。
存储过程里 SQL 没走索引的典型写法
存储过程内部的 SQL 很容易因为参数类型、函数包裹或隐式转换,让优化器“视而不见”已有的索引。
-
WHERE date_col = DATE(in_date):对字段用函数,哪怕date_col有索引也失效;应改写为WHERE date_col >= in_date AND date_col -
WHERE id = in_id,但in_id是VARCHAR(32)而表字段是BIGINT:MySQL 会做CAST(in_id AS SIGNED),触发隐式转换,索引失效 -
WHERE status IN ('a', 'b') OR category = 'x':只要category没索引,整个条件可能退化为全表扫描;拆成UNION ALL更可靠 - 用
SELECT ... INTO @var后再拿@var做 WHERE 条件:优化器无法估算选择率,常放弃索引;换成客户端预编译参数(WHERE col = ?)或直接拼值(如WHERE col = 123)再EXPLAIN
索引建了,但执行计划显示 key_len 很小或 type=range 却扫很多行
这说明索引被用了,但效果差——常见于低选择性字段或复合索引顺序错位。
- 查
gender字段(只有 'M'/'F'),即使建了索引,rows也可能接近总行数;用SELECT COUNT(DISTINCT gender)/COUNT(*) FROM t算选择性,低于 0.1 就别单独建索引 - 建了
INDEX idx_a_b_c (a, b, c),但查询只写WHERE b = 1 AND c = 2:跳过最左列a,索引完全无效;要么补上a条件,要么重建索引为(b, c, a) -
key_len明显小于预期(比如VARCHAR(255)字段只用了 768 字节):可能是前缀索引或 utf8mb4 字符集导致截断,得看SHOW INDEX FROM t确认
临时表没索引,或统计信息陈旧
存储过程里常建 TEMPORARY TABLE 做中间结果,但没人给它加索引;另外,表数据大变后不更新统计信息,优化器会误判成本。
- 建完
#tmp后立刻INSERT INTO #tmp SELECT ...,但后续JOIN或WHERE没走索引:必须手动加,例如CREATE INDEX idx_tmp_x ON #tmp(x) -
EXPLAIN显示rows=100000,但实际只返回 10 行:大概率统计信息过期,跑一次ANALYZE TABLE real_table_name - 过程里反复调用同一段逻辑,但第一次缓存的执行计划不适合后续参数(比如参数从“查 10 行”变成“查 10 万行”):可加
WITH RECOMPILE(SQL Server)或在 MySQL 中避免复用计划(如用PREPARE/EXECUTE动态拼参)
真正卡住的往往不是“有没有索引”,而是“索引能不能覆盖到所有参与运算的字段”以及“优化器能不能看清参数的真实值”。别只盯着 key != NULL 就放心,得看 rows 和 Extra 说了什么——尤其是 Using filesort 或 Using temporary 出现时,说明排序或分组根本没被索引支持。











