索引失效本身不直接拖慢存储过程,真正变慢的是执行计划退化为全表扫描的SQL语句;常见原因包括隐式类型转换(如NVARCHAR2变量匹配VARCHAR2主键)、索引列上使用函数(如TO_CHAR(create_time))、统计信息过期导致绑定变量窥探失效、复合索引未使用前导列触发低效跳跃扫描。
索引失效本身不会直接让存储过程变慢,真正拖慢的是存储过程中执行的 sql 语句——尤其是那些本该走索引却被迫全表扫描的 update、select 或 delete。
隐式类型转换让主键索引彻底失效
这是最常见也最容易被忽略的坑。比如存储过程里用 NVARCHAR2 变量去匹配 VARCHAR2 主键列:
-
UPDATE orders SET status = 'DONE' WHERE order_id = v_order_id;中v_order_id是NVARCHAR2,而表中order_id是VARCHAR2 - Oracle 自动加
TO_CHAR(v_order_id)做隐式转换,导致主键索引无法使用 - 单条语句变慢可能不明显,但循环调用几十次,就变成秒级延迟
- 单独在 SQL*Plus 里执行这条 UPDATE 很快——因为变量类型一致,没触发转换
函数或表达式出现在 WHERE 条件里
存储过程里常把日期处理逻辑写进 SQL,比如:
-
WHERE TO_CHAR(create_time, 'YYYY-MM-DD') = v_date_str—— 即使create_time有索引也没用 - 解决方法不是删函数,而是提前建函数索引:
CREATE INDEX idx_create_date_str ON orders(TO_CHAR(create_time, 'YYYY-MM-DD')); - 注意:11g 不支持基于
SYSDATE的函数索引,TO_CHAR(SYSDATE, ...)这类不能建
统计信息过期 + 绑定变量窥探失效
存储过程多用绑定变量,但 Oracle 11g 的绑定变量窥探(bind peeking)只在硬解析时看一次值。如果第一次传入的是低选择性参数(如 status = 'ALL'),优化器可能生成全表扫描计划并缓存下来,后续即使传入高选择性值(如 status = 'ERROR')也继续复用这个烂计划。
- 查当前游标计划:
SELECT * FROM V$SQL_PLAN WHERE SQL_ID = 'xxx' AND CHILD_NUMBER = 0; - 确认是否用了
FULL TABLE SCAN而非INDEX RANGE SCAN - 临时缓解:加
/*+ INDEX(t idx_status) */提示;长期方案是定期收集统计信息,或启用 SQL Plan Management - 别依赖自动任务——查
DBA_AUTOTASK_CLIENT确认auto optimizer stats collection是否真在运行
复合索引前导列缺失 + 跳跃扫描代价过高
存储过程里常只过滤复合索引的非前导列,比如索引是 (dept_id, emp_id),但代码里只写 WHERE emp_id = ?。
- 11g 支持 Index Skip Scan,但代价远高于普通索引扫描——尤其当
dept_id值少、emp_id值多时,跳扫会遍历每个dept_id分支 - 执行计划里看到
INDEX SKIP SCAN就要警惕,它通常比全表扫描还慢 - 要么补上前导列条件,要么单独为
emp_id建索引,别指望跳跃扫描扛性能
真正卡住存储过程的,从来不是“索引没了”,而是 SQL 执行路径从毫秒级退化到秒级甚至分钟级——而这种退化往往藏在隐式转换、绑定变量、统计信息这些不起眼的地方,一不留神就漏掉。


















