PARTITION RANGE ALL 明确表示优化器放弃分区裁剪,全分区扫描;根本原因是WHERE条件未静态限定分区键,常见于缺失分区键条件、函数包裹分区键、绑定变量未启用ACS、类型隐式转换或统计信息缺失。
执行计划里出现 PARTITION RANGE ALL 就是铁证
这不是“可能没走裁剪”,而是明确告诉你:优化器放弃了分区裁剪,所有分区都被纳入扫描范围。只要 pstart 和 pstop 显示为 all(而不是具体数字或 key),就说明查询条件没能被用来排除任何分区。
常见诱因包括:
- WHERE 条件中压根没写分区键列,比如按
log_time分区,却只写WHERE user_id = 'U123' - 分区键列被套了函数,例如
WHERE trunc(log_time) = date'2025-04-01'—— 即使log_time是分区键,trunc()也直接废掉裁剪能力 - 用了绑定变量但未启用自适应游标共享(ACS),硬解析时无法确定值落在哪个分区,只能保守地扫全分区
分区键类型和查询值类型不匹配也会失效
Oracle 对分区裁剪的判断非常严格,类型隐式转换会破坏裁剪逻辑。比如分区键是 DATE 类型,但查询里传的是字符串:WHERE log_time = '2025-04-01',Oracle 内部会转成 TO_DATE('2025-04-01'),但这个过程可能让优化器无法静态推导分区边界。
务必确保:
- 查询中使用显式类型转换,如
WHERE log_time >= TO_DATE('2025-04-01', 'YYYY-MM-DD') - 绑定变量类型与分区键列完全一致(例如用
DATE绑定变量,而非VARCHAR2) - 避免依赖 NLS 设置做自动转换,比如
WHERE log_time = '01-APR-2025'在不同会话下行为可能不一致
统计信息过期或缺失直接影响裁剪决策
优化器不是靠猜,而是靠统计信息估算每个分区的数据量和分布。如果 DBA_TAB_PARTITIONS 里的 NUM_ROWS 是 NULL 或明显失真,它可能误判“扫一个分区和扫十个差别不大”,干脆放弃裁剪。
检查并修复方式:
- 运行
EXEC DBMS_STATS.GATHER_TABLE_STATS('SCHEMA_NAME', 'TABLE_NAME', granularity => 'ALL');,注意必须带granularity => 'ALL'才会收集分区级统计 - 确认
DBA_TAB_PARTITIONS.NUM_ROWS不为空且数值合理 - 避免长期不收集,尤其在批量插入/截断分区后,统计信息立刻失效
本地索引 + 分区裁剪失败 = 双重性能灾难
本地索引(LOCAL)本身不跨分区,但如果查询没走分区裁剪,就会变成“每个分区都扫一遍索引 + 回表”,I/O 和逻辑读暴增。这时候 direct path read 占比高、CPU 却不高,正是典型症状。
别指望加索引能救裁剪失败:
- 即使
log_time上有本地索引,WHERE user_id = ...仍会触发全分区扫描 - 复合索引如
(user_id, log_time)也无法帮助裁剪,除非log_time在 WHERE 中作为独立条件出现 - 真正该建的是:分区键上单独的本地索引(如
CREATE INDEX idx_log_time ON t(log_time) LOCAL;),但前提是查询先走裁剪
裁剪是前提,索引是加速器——顺序不能颠倒。最容易被忽略的一点:哪怕 SQL 看起来“写了分区键”,只要它被函数包裹、类型不一致、或统计信息不准,裁剪就形同虚设。











