分区裁剪未生效导致全表扫描,主因是WHERE条件未静态推导出目标分区,如分区键被函数包裹、绑定变量值不可知、隐式转换、统计信息未按分区收集、本地索引未覆盖查询字段或分区类型与查询模式错配。

分区表不是“加了就快”的银弹,执行计划退化为 TABLE ACCESS FULL 往往说明分区裁剪(Partition Pruning)根本没生效——你写的 SQL 没触发 Oracle 对分区的识别逻辑,优化器只能当它是一张普通大表来处理。
WHERE 条件没对齐分区键,分区裁剪直接失效
Oracle 只有在 WHERE 条件能静态推导出目标分区时,才会跳过无关分区。一旦条件含函数、绑定变量未显式指定、或用了非等值比较(如 > 但范围过大),裁剪就可能失败。
- 错误写法:
WHERE TO_CHAR(order_date, 'YYYYMM') = '202608'—— 函数包裹分区键,索引和分区都失效 - 正确写法:
WHERE order_date >= DATE '2026-08-01' AND order_date < DATE '2026-09-01' - 绑定变量陷阱:
WHERE part_key = :v1在硬解析时若:v1值不可知(比如 PL/SQL 中未初始化),优化器可能放弃裁剪,尤其在cursor_sharing = force下更危险
统计信息没按分区更新,优化器误判成本
分区表的统计信息必须是“分而治之”的:全局统计 + 各分区独立统计。只收集全局(GRANULARITY => 'GLOBAL')会导致优化器高估单个分区数据量,进而认为走索引+回表比直接扫一个分区还贵。
- 检查命令:
SELECT partition_name, num_rows, last_analyzed FROM dba_tab_partitions WHERE table_name = 'YOUR_TABLE' - 必须执行:
DBMS_STATS.GATHER_TABLE_STATS(..., GRANULARITY => 'AUTO')或显式'PARTITION',不能只靠ESTIMATE_PERCENT => 10全局采样 - 特别注意:交换分区(
EXCHANGE PARTITION)后,新进来的表统计信息不会自动同步,必须手动收集目标分区
本地索引未覆盖查询字段,被迫回表再扫描全分区
本地索引(LOCAL)按分区切分,但它的有效性高度依赖查询是否满足最左前缀 + 是否需要回表。如果 SELECT 列不在索引中,且分区数据量大,优化器可能判定“每个分区都回表取 10 行”不如“直接扫一个分区取全部”。
- 现象:
INDEX RANGE SCAN后跟TABLE ACCESS BY LOCAL INDEX ROWID,但后者实际读了整个分区的数据块 - 验证方法:用
DBMS_XPLAN.DISPLAY_CURSOR查看Rows和A-Rows是否严重偏离(比如预估 100 行,实际返回 50 万行) - 解决方向:要么扩展本地索引为覆盖索引(
INCLUDE或追加列),要么确认查询是否真需要那么多列——SELECT *是本地索引杀手
分区类型与查询模式错配,物理结构反而拖累
范围分区适合时间类等值/范围查询;列表分区适合离散枚举;哈希分区只支持等值查询。若用哈希分区存时间字段,又写 WHERE create_time > SYSDATE-7,那必然全分区扫描。
- 典型错配:
LIST分区按地区代码,但查询写成WHERE region LIKE 'CN%'—— 无法裁剪 - 隐式转换风险:
WHERE part_key = 123(part_key是VARCHAR2),触发隐式转数字,分区键失准 - 子分区陷阱:复合分区(如
RANGE-LIST)下,只满足外层范围条件,不满足内层列表条件,仍可能扫描多个子分区
真正卡住性能的,往往不是分区本身,而是你没让 Oracle “看见”分区边界——它不知道哪些分区可以跳过,就只能老老实实从第一个块扫到最后一个块。裁剪是否生效,永远以 Predicate Information 部分的 partition start / partition stop 为准,而不是你以为写了分区键就万事大吉。


















