exchange partition后统计信息不会自动迁移,原分区统计清零、新表变空白,需手动执行dbms_stats.gather_table_stats并指定granularity=>'partition'、force=>true、degree=>auto_degree,同时重建索引,且须先收集统计再重建索引。

EXCHANGE PARTITION后统计信息不会自动迁移
交换分区本身不复制、不继承统计信息,原分区的NUM_ROWS、LAST_ANALYZED等元数据直接清零,新表(原源表)变成“统计信息空白状态”。这不是 bug,是 Oracle 的设计行为 —— 它只换段指针,不碰统计信息字典。
典型现象:SELECT TABLE_NAME, PARTITION_NAME, LAST_ANALYZED FROM USER_TAB_PARTITIONS WHERE TABLE_NAME = 'ORDERS' 返回大量 NULL;执行查询时优化器误判为空表,走全表扫描而非分区裁剪。
- 别指望
INCLUDING INDEXES或WITHOUT VALIDATION附带统计信息更新 - 即使源表(如
STAGING_TABLE)已有完整统计信息,交换后也完全丢失 -
DBMS_STATS.GATHER_SCHEMA_STATS默认跳过已存在且“看似有统计信息”的表,不会主动重刷
必须手动调用 DBMS_STATS.GATHER_TABLE_STATS 并指定 granularity
默认参数下 DBMS_STATS.GATHER_TABLE_STATS 只收集表级('GLOBAL')统计信息,对分区表而言,这会导致 USER_TAB_PARTITIONS.LAST_ANALYZED 仍为 NULL,分区裁剪失效。
关键参数缺一不可:
-
granularity => 'PARTITION':强制逐个分区收集,填满USER_TAB_PARTITIONS.LAST_ANALYZED -
force => TRUE:绕过“已有统计信息”的跳过逻辑(尤其适用于交换后源表非首次收集) -
degree => DBMS_STATS.AUTO_DEGREE:避免单线程慢得离谱,尤其在大分区场景
示例:
BEGIN
DBMS_STATS.GATHER_TABLE_STATS(
ownname => 'SCHEMA',
tabname => 'ORDERS_ARCH_20240501',
granularity => 'PARTITION',
force => TRUE,
degree => DBMS_STATS.AUTO_DEGREE
);
END;
本地索引交换后状态为 UNUSABLE,需单独处理
INCLUDING INDEXES 只把索引分区“挪过去”,但状态会变成 UNUSABLE,此时即使统计信息正确,查询也可能因索引不可用而退化为全表扫描。
- 本地索引:执行
ALTER INDEX idx_name REBUILD PARTITION partition_name - 全局索引:交换后直接失效,若未加
UPDATE GLOBAL INDEXES,必须重建整个索引(ALTER INDEX idx_name REBUILD) - 别依赖
CASCADE => TRUE自动处理索引状态 —— 它只管统计信息,不管可用性
增量统计信息不是万能的,交换后仍需显式触发
即使表启用了增量统计信息(INCREMENTAL => TRUE),EXCHANGE PARTITION 不会自动触发 synopsis 更新或全局统计信息重算。Oracle 只在后续 DML 触发自动收集时才介入,而交换后的空窗期足够让执行计划出错。
- 增量统计信息的作用是加速后续小批量变更的统计更新,不是兜底机制
- 交换完成后的第一时间,必须人工触发
DBMS_STATS.GATHER_TABLE_STATS,不能等自动任务 - 若使用
GRANULARITY => 'AUTO',在 19c 中行为不稳定,明确写'PARTITION'更可靠
最易被忽略的点:统计信息收集和索引重建必须成对执行,且顺序不能颠倒 —— 先收集统计信息再重建索引,否则重建过程中优化器可能基于错误基数生成低效计划。











