分区裁剪是否生效需重点核查predicate information中的access predicates,函数、类型不匹配、or条件等会导致失效;awr中高gets低a-rows是典型征兆;display_cursor的starts和partitions accessed字段才是裁剪验证金标准。
为什么分区裁剪没生效,但执行计划里看不出问题
分区裁剪(partition pruning)是否发生,不能只看 operation 列是否出现 partition range single 或 inlist iterator。真正关键的是 predicate information 部分里的访问谓词(access predicates)——只有落在分区键上的等值、范围或 in-list 条件才可能触发裁剪;而过滤谓词(filter predicates)哪怕写在 where 里,也**不会减少扫描的分区数**。
常见误判场景:
- 分区键是
CREATE_TIME(DATE 类型),但 SQL 写成TRUNC(CREATE_TIME) = DATE '2026-04-01':函数导致分区键失效,优化器无法推导出具体分区 - 绑定变量类型不匹配,比如传入
NUMBER给VARCHAR2分区键,隐式转换使分区裁剪失效 - 使用了
OR连接多个条件,其中一部分无法裁剪,整个 range 就退化为全扫
从 AWR 报告里定位疑似未裁剪的 SQL
AWR 报告本身不直接标出“未裁剪”,但能暴露典型征兆:高逻辑读 + 低 A-Rows,尤其在大分区表上。重点看两个地方:
-
SQL ordered by Gets页面:找那些Buffer Gets超过百万、但Executions很低、Rows Processed却只有几百或几千的 SQL —— 这大概率扫了几十个甚至上百个分区,只返回少量数据 -
Instance Activity Stats下的physical reads和db block gets如果与user calls比例严重失衡,也暗示底层做了大量无谓分区扫描
拿到可疑 sql_id 后,别急着看报告里的摘要计划,它通常只显示 TYPICAL 格式,会隐藏 PREDICATE INFORMATION。必须用 DBMS_XPLAN.DISPLAY_AWR 补全:
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_AWR('abc123xyz', NULL, NULL, 'ADVANCED'));
注意第三参数留空表示查所有快照,实际建议加时间范围限制,否则输出太长。
用 DISPLAY_CURSOR 看真实执行时的裁剪行为
如果 SQL 还在共享池里(V$SQL 中能找到),用 DISPLAY_CURSOR 比 DISPLAY_AWR 更准,因为它反映的是**刚执行过的实际行为**,包括是否真的只访问了目标分区。
- 先确认 SQL 已执行过:
SELECT sql_id, child_number, plan_hash_value, executions FROM v$sql WHERE sql_text LIKE '%your_table_name%'; - 再查真实计划:
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('abc123xyz', 0, 'ALLSTATS LAST'));
重点关注两列:
-
Starts:如果值是 100,而你的表有 100 个分区,基本就是全扫;如果是 1 或 3,说明裁剪有效 -
A-Rows和Buffers的比值:若A-Rows是 100,Buffers是 500000,说明每行平均读了 5000 块 —— 典型的跨多个分区扫描后过滤
容易被忽略的隐式陷阱
即使执行计划显示 PARTITION RANGE ITERATOR,也不代表裁剪一定成功。下面这些情况会让裁剪形同虚设:
- 分区键上有函数索引,但查询没走该索引(比如用了不同函数或类型),优化器 fallback 到全扫分区
- 统计信息陈旧,特别是
USER_TAB_PARTITIONS.NUM_ROWS为空或严重不准,优化器误判成本,放弃裁剪路径 - RAC 环境下某节点缓存了错误的分区统计,导致同一
sql_id在不同实例上计划不同 —— 此时PLAN_HASH_VALUE可能一样,但实际扫描分区数差几倍
最稳妥的方式,是结合 DBA_TAB_PARTITIONS 和执行计划里的 Partitions accessed 字段交叉验证,而不是只信计划树结构。











