直接路径批量加载后分区表的分区级统计信息不会自动更新,因为Oracle 12c在线统计仅在表初始为空且使用直接路径插入时触发,且只收集全局和列级统计,跳过分区级关键指标如NUM_ROWS、BLOCKS等。
直接路径批量加载后,分区表的分区级统计信息不会自动更新,必须手动调用 dbms_stats.gather_table_stats 指定分区收集。
为什么分区表不能靠在线统计自动覆盖?
Oracle 12c 的在线统计(Online Statistics Gathering)只在满足两个前提时触发:表初始为空 + 使用直接路径插入(如 INSERT /*+ APPEND */ 或 CREATE TABLE AS SELECT)。即便如此,它也只收集全局(GLOBAL)和列级基础统计,完全跳过每个分区的 NUM_ROWS、BLOCKS、AVG_ROW_LEN 等关键指标。分区级缺失会导致优化器误判分区剪枝效果,尤其在 WHERE partition_key = :val 场景下可能选错执行计划。
调用 GATHER_TABLE_STATS 收集单个分区的正确写法
必须显式指定 partname 参数,且不能依赖默认行为:
-
ownname和tabname必须为大写(除非建表时加双引号),否则报ORA-20000: Unable to get table statistics -
partname同样需大写,且必须与DBA_TAB_PARTITIONS.PARTITION_NAME完全一致 - 省略
degree会退化为串行,大数据量分区建议设为DBMS_STATS.AUTO_DEGREE或具体数值(如 4) - 不设
method_opt时,默认只收集列基础统计(FOR ALL COLUMNS SIZE AUTO),若需直方图,得显式追加
示例(收集 SALES 表的 P2023_Q4 分区):
EXEC DBMS_STATS.GATHER_TABLE_STATS( ownname => 'SH', tabname => 'SALES', partname => 'P2023_Q4', degree => 4, method_opt => 'FOR ALL COLUMNS SIZE SKEWONLY', cascade => FALSE );
批量收集多个分区时的并发陷阱
即使设置了全局并发开关 CONCURRENT='ALL',GATHER_TABLE_STATS 在处理单个分区表的多个分区时仍强制串行化——Oracle 内部会锁住整个表对象,逐个分区执行。这意味着:
- 用循环遍历
DBA_TAB_PARTITIONS调用多次GATHER_TABLE_STATS,不会真正并发 - 想提速,得把不同分区表拆到不同 job 中(例如
SALES的 P1 和CUSTOMERS的 P1 可并行) - 若强行提高
JOB_QUEUE_PROCESSES,反而可能因资源争用拖慢整体耗时
更稳妥的做法是:先用 DBA_TAB_PARTITIONS 筛出 STALE_STATS = 'YES' 的分区,再按表分组,对每组表单独发起一个带 partname 的收集调用。
容易被忽略的直方图与索引统计缺口
在线统计和默认的 GATHER_TABLE_STATS 都不收集直方图和索引统计。如果分区中某列数据倾斜严重(如状态码只有 'A'/'B'/'C',但 'A' 占 95%),缺少直方图会让优化器低估谓词选择性。此时必须:
- 在
method_opt中明确指定SKEWONLY或AUTO,不能依赖默认值 - 索引统计需额外调用
DBMS_STATS.GATHER_INDEX_STATS,且同样要指定partname(针对分区索引) -
cascade => TRUE不会自动触发分区索引统计,仅作用于非分区索引
分区统计这件事,从来不是“跑完一个命令就万事大吉”——它要求你清楚知道哪几个分区变了、哪些列分布异常、索引是否也该同步更新,缺一不可。











