PL/SQL中Hint必须紧贴SELECT/INSERT/UPDATE/DELETE关键字,否则失效;动态SQL需将Hint拼入字符串;函数内联、统计信息缺失、自适应计划等均可能导致Hint失效,务必用EXPLAIN PLAN验证实际执行路径。
PL/SQL里写Hint必须紧贴SELECT/INSERT/UPDATE/DELETE关键字
在pl/sql块中嵌入的sql语句,hint不是随便加在哪儿都生效的。最常见的失效原因是把hint写在了变量声明后、或begin之后但没紧跟dml关键字。oracle只认select、insert、update、delete后面紧挨着的/*+ hint */注释。
- ✅ 正确:
SELECT /*+ INDEX(t idx_emp_dept) */ empno FROM emp t WHERE deptno = 10; - ❌ 错误:
DECLARE v_name VARCHAR2(20); SELECT /*+ INDEX(t idx_emp_dept) */ ...—— 中间有声明,Hint被忽略 - ❌ 错误:
SELECT /* + INDEX(t idx_emp_dept) */ ...——/* +中间有空格,+必须紧贴/* - 如果用了表别名(比如
t),Hint里也必须用别名,写/*+ INDEX(emp idx_emp_dept) */会无效
动态SQL中Hint要拼进字符串,不能靠外部注释
用EXECUTE IMMEDIATE执行的动态SQL,Hint必须作为字符串一部分拼进去,外面加注释完全无效。很多人以为在PL/SQL块里加个注释就能影响里面EXECUTE IMMEDIATE的执行计划,实际不会。
- ✅ 正确:
v_sql := 'SELECT /*+ FULL(e) */ ename FROM emp e WHERE sal > :1'; EXECUTE IMMEDIATE v_sql USING 5000; - ❌ 错误:
--+ FULL(e) EXECUTE IMMEDIATE 'SELECT ename FROM emp e WHERE sal > :1' USING 5000;—— 注释在EXECUTE IMMEDIATE前,对内部SQL无影响 - 注意单引号转义:拼Hint时若含单引号(如
'/*+ INDEX(t "IDX_NAME") */'),需用两个单引号''处理
函数内联(inlining)可能让Hint失效
Oracle 19c默认启用PL/SQL函数内联优化(PLSQL_OPTIMIZE_LEVEL=2),如果Hint写在被内联的子程序里,编译后可能被重组,导致Hint位置偏移或丢失。
- 检查是否内联:
SELECT plsql_optimize_level FROM user_plsql_object_settings WHERE name = 'YOUR_PROC'; - 临时禁用内联:
ALTER PROCEDURE your_proc COMPILE PLSQL_OPTIMIZE_LEVEL=1; - 更稳妥的做法:把带Hint的关键查询单独抽成独立的SQL语句(不封装进函数),或改用
PRAGMA INLINE显式控制 - 特别注意:
WITH子句里的Hint,在19c中若配合函数内联,容易被优化器忽略,建议直接写在主SELECT上
绑定变量与Hint共存时,统计信息必须有效
Hint能压倒优化器选择,但前提是CBO有可靠依据——也就是表和索引的统计信息必须存在且不过期。19c中如果统计信息缺失或STALE,即使写了/*+ INDEX(t idx) */,也可能退化为全表扫描(尤其当优化器估算索引选择性极差时)。
- 验证统计信息:
SELECT stale_stats, last_analyzed FROM user_tab_statistics WHERE table_name = 'EMP'; - 强制收集:
DBMS_STATS.GATHER_TABLE_STATS('SCOTT', 'EMP', method_opt=>'FOR ALL INDEXED COLUMNS'); - 避免用
/*+ RULE */:19c已废弃RBO,该Hint会被忽略,且触发警告ORA-38500 -
FULL和INDEX互斥,但INDEX指定多个索引时,Oracle仍可能选其中一个——不是“必须用你写的那个”,而是“必须从你列的里面选”
实际调优时最易被忽略的是:Hint只作用于当前SQL文本,不跨PL/SQL作用域;而19c的自适应执行计划、SQL Plan Directives等机制,可能覆盖Hint效果——所以加完Hint后,务必用EXPLAIN PLAN FOR + DBMS_XPLAN.DISPLAY确认真实执行路径,不能只看代码里写了什么。


















