子查询解嵌套在Oracle 9i+默认开启,优化器自动将IN/EXISTS等转为半连接或反连接;但含ROWNUM、分层查询、跨层相关列等语义受限时不可解嵌套;可用UNNEST提示强制,NO_UNNEST或OPT_PARAM临时禁用。

子查询解嵌套默认是开启的,不用额外启用
Oracle 9i 及以后版本中,_unnest_subquery 隐含参数默认值就是 TRUE,也就是说子查询解嵌套(Subquery Unnesting)在绝大多数情况下已经自动生效。你不需要执行任何操作,优化器就会尝试把 IN、EXISTS、NOT IN 等子查询转为半连接(semi-join)、反连接(anti-join)或内联视图(inline view),从而避免逐行执行 FILTER 操作。
哪些子查询能被解嵌套,取决于语义和结构限制
即使参数开着,也不是所有子查询都能被解嵌套。常见被拒绝的情况包括:
-
ROWNUM出现在子查询中(如SELECT * FROM t WHERE id IN (SELECT id FROM s WHERE ROWNUM = 1)) - 子查询含聚合函数且无
GROUP BY(如WHERE x > (SELECT MAX(y) FROM t)是允许的;但带GROUP BY+ 相关列就可能失败) - 子查询使用了集合运算符(
UNION、INTERSECT、MINUS),除非是 Oracle 11.2+ 且满足特定条件 - 子查询引用了“非直接外部块”的相关列(比如三层嵌套里,最内层引用了最外层的列)
- 子查询是分层查询(含
CONNECT BY)
这些限制不是配置问题,而是语义等价性决定的——解嵌套后 SQL 行为不能变,否则优化器会主动跳过。
用 UNNEST hint 强制解嵌套特定子查询
当优化器因成本估算保守而放弃解嵌套时,可以用 UNNEST 提示干预。它比全局参数更精准,也更安全:
- 只作用于带提示的那个子查询,不影响其他部分
- 适用于 Oracle 10gR2+,语法是
SELECT /*+ UNNEST */ ... FROM ... WHERE x IN (SELECT /*+ UNNEST */ y FROM t ...) - 若子查询本身不支持解嵌套(比如含
ROWNUM),加UNNEST会被忽略,不会报错 - 配合
NO_UNNEST可做对比测试:加了NO_UNNEST后执行计划通常出现FILTER或NESTED LOOPS,方便验证性能差异
示例:
SELECT ename FROM emp WHERE deptno IN ( SELECT /*+ UNNEST */ deptno FROM dept WHERE loc = 'DALLAS' );
禁用解嵌套要小心,仅限诊断或特殊场景
极少数情况需要关掉解嵌套,比如怀疑某次解嵌套引入了错误执行路径,或想复现旧版执行计划。可用方式有:
- 会话级:
ALTER SESSION SET "_unnest_subquery" = FALSE; - 语句级(推荐):
SELECT /*+ OPT_PARAM('_unnest_subquery', 'FALSE') */ ... - 更彻底:
SELECT /*+ NO_QUERY_TRANSFORMATION */ ...(同时禁用所有查询转换)
注意:_unnest_subquery 是隐含参数,生产环境不建议长期关闭;NO_QUERY_TRANSFORMATION 影响面太大,容易让原本可优化的语句退化成全表扫描。
真正难处理的,往往不是“怎么开”,而是“为什么没开”——得先看执行计划里有没有 VW_SQ_* 视图名,再查 DBMS_XPLAN.DISPLAY_CURSOR 的 Predicate Information 部分,确认是否真被跳过,以及被什么规则拦住。


















