分区消除只认分区键,不认WHERE里的任意列;执行计划能否剪枝完全取决于查询条件是否能静态推导出目标分区范围,且仅基于PARTITION BY列,与索引无关。
分区消除只认分区键,不认WHERE里的任意列
oracle 执行计划中能否做分区消除(partition pruning),完全取决于查询条件是否能静态推导出目标分区范围。这个推导过程只基于表定义的 partition by 列(即分区键),和索引无关。哪怕你在 where 里写了 status = 'active',只要 status 不是分区键,优化器就无法跳过无关分区。
常见错误现象:
- 表按
created_date范围分区,但查询只写WHERE order_id = 12345→ 全分区扫描 - 表按
(country_code, region)多列列表分区,但查询只过滤region = 'TOKYO'→ 无法剪枝,必须扫描所有country_code下的分区 - 使用函数包装分区键,如
WHERE TRUNC(created_date) = DATE '2025-01-01'→ 即使列本身是分区键,TRUNC 也会让剪枝失效(除非建函数索引且启用扩展统计)
本地索引不能“代替”分区键参与剪枝
有人误以为建个本地索引就能触发分区消除,其实不然。本地索引只是让每个分区有自己独立的索引段,它本身不改变剪枝逻辑。
关键点:
-
WHERE条件必须包含分区键的等值或范围谓词,且不能被函数、表达式、绑定变量隐式屏蔽 - 本地前缀索引(如索引列以分区键开头)有助于在剪枝后快速定位行,但它不是剪枝的前提
- 本地非前缀索引(如只索引
amount)对剪枝毫无帮助;即使该索引命中,仍需访问所有分区去查对应amount值的行
示例:
表 sales 按 sale_month 范围分区,建了本地索引 idx_sales_amount(仅含 amount)。
执行 SELECT * FROM sales WHERE amount > 10000 → 依然全分区扫描,因为 amount 不是分区键。
为什么加了索引还是慢?可能根本没剪枝
很多性能问题本质是“以为剪枝了,其实没剪”。典型信号包括:
- 执行计划中
PARTITION RANGE ALL或PARTITION LIST ALL出现在访问路径里 -
DBA_TAB_PARTITIONS显示表有 12 个分区,但V$SQL_PLAN的OBJECT_NAME列显示访问了全部 12 个分区名 - 启用
ALTER SESSION SET TRACEFILE_IDENTIFIER='prune';+EVENT 10046,看 trace 文件里实际读了哪些分区文件
排查建议:
- 用
EXPLAIN PLAN FOR ...后查DBMS_XPLAN.DISPLAY,重点看PARTITION START和PARTITION STOP是否为具体分区号(如KEY或ROW LOCATION表示失败) - 确保绑定变量类型与分区键列类型严格一致(比如
DATE列别传VARCHAR2字符串) - 避免在分区键上用
IS NULL、!=、NOT IN等否定操作,它们通常禁用剪枝
分区剪枝不是“有索引就自动生效”的智能机制,它是编译期基于字面量/确定性表达式做的硬匹配。一旦条件脱离分区键,再深的索引也救不了扫描。











