标量子查询不参与oracle的result_cache机制,因其嵌入父查询中而非独立查询块;它依赖buffer cache和shared pool缓存,但每次执行仍需重新计算。

SQL 标量子查询本身不参与 Oracle 的结果集缓存(RESULT_CACHE)机制,无论你加提示、调参数还是改语句结构——它压根不会被缓存。这不是配置漏了,也不是权限不对,而是 Oracle 内部执行路径决定的。
RESULT_CACHE 不缓存标量子查询的原因
Oracle 的 RESULT_CACHE 只对顶层 SELECT 语句的完整结果集生效,且要求该语句是“可独立执行的查询块”。而标量子查询(scalar subquery)是作为表达式嵌入在父查询中的,例如:
SELECT empno, ename,
(SELECT dept_name FROM dept WHERE dept.deptno = emp.deptno) AS dept_name
FROM emp;
这种写法里,括号内的子查询:
- 不构成独立的执行计划节点(不是
SELECT语句的根) - 不触发
RESULT_CACHE的注册逻辑(即不会出现在V$RESULT_CACHE_OBJECTS中) - 即使你在子查询里硬加上
/<em>+ RESULT_CACHE </em>/,优化器也会直接忽略——语法上允许,但语义上无效
你查 V$RESULT_CACHE_OBJECTS 或看执行计划,永远看不到标量子查询对应的缓存条目或 RESULT CACHE 操作符。
标量子查询实际依赖哪种缓存
它真正受益的是 Buffer Cache(数据库缓冲区缓存) 和 Shared Pool 中的游标缓存(library cache):
- 子查询 SQL 文本会被硬解析一次,后续复用游标(
V$SQL中可查EXECUTIONS和PARSE_CALLS) - 子查询访问的
dept表数据块,如果频繁读取,会留在 Buffer Cache 中,减少物理读 - 但它的结果值不会被缓存:每次外查询扫描一行
emp,子查询都重新执行(哪怕参数相同),除非 Oracle 启用了「标量子查询缓存」(Scalar Subquery Caching)——但这和RESULT_CACHE完全无关,是优化器内部的轻量级行级缓存,仅限于单次 SQL 执行过程中对相同输入值的去重调用
你可以验证这点:
- 在子查询中加
DBMS_OUTPUT.PUT_LINE(需 PL/SQL 包裹)或触发器,会发现它被调用多次 - 查
V$SQL,父查询的EXECUTIONS是 1,但子查询对应游标的EXECUTIONS可能远大于 1(取决于emp行数)
如何让标量子查询“变快”,而不是指望缓存
如果你发现标量子查询拖慢主查询,优先考虑这些实操手段:
-
改写为 ANSI JOIN:把子查询转成
LEFT JOIN,让优化器有机会使用哈希连接或嵌套循环,避免每行都执行一次子查询 -
确保关联字段有索引:比如
dept(deptno)必须有索引,否则子查询变成全表扫描,性能雪崩 -
检查是否真需要标量语义:如果子查询可能返回多行,当前写法会报
ORA-01427;不如提前用聚合或MAX()显式控制,也方便优化器估算 -
用物化视图预计算:如果
dept表极小且稳定,建一个物化视图mv_dept_lookup并启用查询重写,让优化器自动把子查询重写为对 MV 的快速访问(注意:这和RESULT_CACHE依然互斥)
最常被忽略的一点是:标量子查询的“缓存感”往往来自开发者的错觉——你以为它被缓存了,其实只是 Buffer Cache 把 dept 块保住了,或者 Shared Pool 复用了游标。真正的结果值从未进过 RESULT_CACHE 内存池。











