是,同一SQL_ID在ASH中若出现多个sql_plan_hash_value,说明自适应执行计划发生分支切换;需结合session_state='ON CPU'、sql_plan_operation与sql_plan_options定位真实执行路径,并通过dba_hist_sql_plan的other_xml验证是否为自适应计划。

查ASH里同一SQL_ID的sql_plan_hash_value是否跳变
自适应执行计划切换时,sql_plan_hash_value 会随实际走的分支变化而改变——这不是采样误差,是优化器真实重选了物理路径。在 gv$active_session_history(RAC必须用gv$)中执行:
SELECT sql_id, sql_plan_hash_value, COUNT(*) cnt FROM gv$active_session_history WHERE sql_id = 'your_sql_id' AND sample_time BETWEEN ... AND ... GROUP BY sql_id, sql_plan_hash_value ORDER BY cnt DESC;
若返回多个 sql_plan_hash_value,说明运行中发生了分支切换。注意:不同值对应不同子计划(如 HASH JOIN vs NESTED LOOPS),但它们共享同一组 operation_id。
用sql_plan_operation + sql_plan_options定位真实执行分支
v$active_session_history 不存计划结构,但每条采样记录都带 sql_plan_operation 和 sql_plan_options,这才是运行时“真正在干啥”的证据。
- 比如一个自适应联接节点,编译时有两条子计划:
NESTED LOOPS(默认)和HASH JOIN(备选) - 若采样中反复出现
sql_plan_operation = 'HASH JOIN'且sql_plan_options = 'JOIN',就说明实际激活的是 HASH 分支 - 别只看
sql_plan_operation = 'JOIN'这种泛称——它不区分类型;必须搭配sql_plan_options或具体操作名才可靠
过滤 ON CPU 样本,排除等待事件干扰
自适应决策点常发生在前几万行扫描后,此时真正耗时的是计算本身,不是等待。如果混入大量 DB file sequential read 或 enq: TX 样本,会掩盖真实执行路径的分布。
- 加条件
session_state = 'ON CPU',聚焦纯计算路径 - 避免用
time_waited判断分支耗时——它统计等待,不是操作执行时间 - 更准的方式是看同一
sql_exec_id下各operation_id的样本密度,密度高即该节点实际承担了主要工作
关联 dba_hist_sql_plan 确认 plan_hash_value 变化是否来自自适应
仅靠 ASH 看到多个 sql_plan_hash_value 还不够,得确认这些 hash 值是否真的对应自适应计划里的不同子路径,而不是统计信息变更或绑定值漂移导致的常规计划变更。
- 用
sql_id关联dba_hist_sql_plan,查对应plan_hash_value的other_xml字段 - 执行
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_AWR('sql_id', plan_hash_value, NULL, '+adaptive')) - 输出中必须含
this is an adaptive plan且有带-标记的 inactive 行,才能断定是自适应切换,不是普通计划漂移
自适应抖动最难抓的一点是:它不总在慢的时候发生,有时快分支切得早、整体还快;有时慢分支被误激活,但单次执行耗时又没超阈值,AWR 里根本不会报。这时候,只有秒级采样的 ASH 能把切换瞬间钉死在时间轴上。


















