存储过程索引不走八成因绑定变量与字段类型不匹配导致隐式转换;查执行计划Predicate Information确认TO_NUMBER/TO_CHAR等转换,用字面量对比验证,查V$SQL_BIND_CAPTURE核对真实类型,参数声明须严格一致。
存储过程里索引不走,八成是绑定变量类型和字段类型对不上,不是没建索引,而是sql在执行时悄悄给字段套了to_number或to_char。
怎么看是不是隐式转换惹的祸
别猜,直接看执行计划里的Predicate Information区域:
- 出现
access("ID"=TO_NUMBER(:B1))→ 字段是NUMBER,但绑定变量:B1传的是字符串 - 出现
filter(TO_CHAR("CREATED_DATE")='20240101')→ 字段是DATE,却对它用TO_CHAR包装后比对 - 出现
COLLATE "USING_NLS_COMP"→VARCHAR2字段因NLS设置触发字符集隐式转换
更准的验证方式:把存储过程里所有:p_id这类绑定变量临时替换成字面量(比如'123'或123),再跑EXPLAIN PLAN FOR。如果字面量能走索引、变量不能,基本就是类型不一致。
查V$SQL_BIND_CAPTURE确认变量真实类型
光看代码声明不够,PL/SQL有时会静默转类型。运行完存储过程后,查:
SELECT name, datatype_string, value_string FROM V$SQL_BIND_CAPTURE WHERE sql_id = '你的SQL_ID' AND child_number = 0;
重点核对datatype_string是否和目标字段类型一致:
- 字段是
NUMBER→ 绑定变量datatype_string必须是NUMBER,不能是VARCHAR2 - 字段是
DATE→ 变量类型必须是DATE,不能是VARCHAR2再靠TO_DATE转 - 字段是
VARCHAR2(32)→ 变量也得是VARCHAR2,且长度足够,避免截断引发二次转换
存储过程里怎么写才稳
核心就一条:让索引列在WHERE里“裸奔”,不被任何函数包裹。具体落地:
- 参数声明类型必须和字段类型严格一致:
PROCEDURE get_user(p_id IN NUMBER),而不是全用VARCHAR2图省事 - 日期参数统一用
IN DATE,别收VARCHAR2再TO_DATE;如果前端只能传字符串,在入口就做一次显式转换:v_date := TO_DATE(p_date_str, 'YYYY-MM-DD');,后续SQL只用v_date - 游标参数也一样:如果游标
cur_emp定义为FOR SELECT * FROM emp WHERE id = :p_id,那调用时OPEN cur_emp(123)必须传NUMBER,不能传'123' - 动态SQL拼接要警惕
||:比如'WHERE id = ' || p_id,若p_id是VARCHAR2,拼出来就是WHERE id = '123',触发隐式转换;应改用绑定变量方式执行
最容易被忽略的细节
不是所有类型不匹配都会立刻报错,有些转换在PL/SQL层就完成了,到SQL引擎时已经“面目全非”。比如声明v_id VARCHAR2(10) := '123';,再传给NUMBER字段查询,PL/SQL会自动转成TO_NUMBER(v_id),但这个转换发生在SQL解析前,执行计划里看到的已经是TO_NUMBER(:B1) —— 这类问题必须结合V$SQL_BIND_CAPTURE和DUMP(p_id)一起看,单看代码根本发现不了。


















