运维管理30 分钟阅读
Oracle 分区表维护与性能排查 100 条命令
分区表把一张大表按分区键拆成可独立维护的片段。查慢 SQL 时要看执行计划有没有裁剪到目标分区;做归档或分区交换时,又得确认局部索引、全局索引和统计信息。仅凭表上建了分区,就认定查询只扫一个分区,很容易误判。这篇把分区元数据、裁剪、索引和维护操作放在一起,建议收藏,遇到表增长或老数据归档时顺着查。
2026年9月16日阅读—点赞—收藏—
dba100oraclescenario
100 条命令系列文章专栏
分区表把一张大表按分区键拆成可独立维护的片段。查慢 SQL 时要看执行计划有没有裁剪到目标分区;做归档或分区交换时,又得确认局部索引、全局索引和统计信息。仅凭表上建了分区,就认定查询只扫一个分区,很容易误判。这篇把分区元数据、裁剪、索引和维护操作放在一起,建议收藏,遇到表增长或老数据归档时顺着查。
分区表把一张大表按分区键拆成可独立维护的片段。查慢 SQL 时要看执行计划有没有裁剪到目标分区;做归档或分区交换时,又得确认局部索引、全局索引和统计信息。仅凭表上建了分区,就认定查询只扫一个分区,很容易误判。这篇把分区元数据、裁剪、索引和维护操作放在一起,建议收藏,遇到表增长或老数据归档时顺着查。
示例基于 Linux 和 Oracle Database 19c,表名和日期边界按现场替换。ALTER TABLE 类命令会影响数据或索引状态,先在测试表演练再进生产窗口。
Oracle RANGE 分区裁剪与索引关系
这张图以单层 RANGE 月分区为例。日期范围可以裁剪到目标分区;局部索引与表分区对应,但 SQL 是否走索引还要看真实计划。全局索引单独维护,分区 DDL 后要查它的状态。
先区分 RANGE、LIST、HASH 与复合分区。复合分区表的顶层分区可能只保存元数据,真正的数据段在子分区上,后续空间检查不能只数顶层分区。
慢 SQL 的谓词若没有落在分区键上,就不能期待仅靠分区完成静态裁剪。分区键上的函数和隐式类型转换也可能让裁剪效果变差。
NUM_ROWS 来自统计信息,不是实时行数。大量数据刚写入新分区却未收集统计时,优化器看到的行数可能滞后;后续要把分区级统计与执行计划一起看。
LOCAL 索引跟随表分区维护,GLOBAL 索引有自己的分区组织。删除或交换表分区前必须分清索引类型;不能默认所有索引都会自动保持可用。
复合分区表的数据段在子分区中。空结果要先确认这张表是不是复合分区表,不能把“没有子分区”误报成目标月份没有数据。
复合分区要同时知道顶层分区键与子分区键。只按月份命中顶层分区,子分区若按地区或 hash 划分,实际访问范围还取决于第二层键和谓词。
HIGH_VALUE 是 LONG 类型,在 SQL*Plus 中可以直接显示,但不能像普通 VARCHAR2 一样随手用 SUBSTR、TO_DATE 处理。维护前把边界表达式与业务日期约定对上,特别注意上界是 LESS THAN。
INTERVAL 非空时,新边界可能随插入自动产生。间隔分区表的 PARTITION_COUNT 不能直接当实际分区数量;实际有哪些分区仍看第 3 条。
这是已分配段空间,不是表中有效数据量。复合分区表通常显示 TABLE SUBPARTITION;延迟创建段的空分区可能没有记录,不应把它误判成字典丢了分区。
简单分区表的 SEGMENT_CREATED=NO 可以是延迟段创建的正常状态。复合分区顶层这个字段表示给子分区的默认创建方式,可能是 NONE、YES 或 NO;要判断实际段是否存在,应查子分区和第 9 条的段清单。
查到 UNUSABLE 后先确认是哪次维护操作造成的,再安排重建。复合分区上的局部索引还可能落在索引子分区,顶层分区 STATUS 不足以覆盖所有叶子段。
删除或截断表分区后,全局索引分区可能出现 ORPHANED_ENTRIES=YES。这与 STATUS=UNUSABLE 不是一回事;维护后要分别检查索引可用性和延迟清理的条目。
看 PSTART、PSTOP 和 PARTITION RANGE。如果起止分区只覆盖目标月份,说明优化器计划裁剪;KEY 代表运行时才确定分区。EXPLAIN PLAN 是估算计划,线上真实 SQL 还要查实际游标计划。
分区键上直接套函数通常使静态裁剪失效,计划里可能出现 PARTITION RANGE ALL。这里用来对照第 13 条,不能只看 SQL 结果相同就认定读了相同数量的分区。
线上 SQL 以实际游标计划为准,重点看 PSTART、PSTOP 与谓词信息。ALLSTATS LAST 要有执行统计信息才会显示实际行数;没有 A-Rows 时,不能拿估计行数冒充实际扫描量。
这条明确只查指定分区,适合对照日期边界和业务数据。它会实际扫描分区,生产大表要评估 I/O;它不是验证普通 SQL 是否自动裁剪的证据,后者仍看真实游标计划。
DATE 分区键用字符串参数比较时可能发生隐式转换。先看列类型,再调整应用绑定变量或 SQL 字面量;不能把一次 EXPLAIN PLAN 上的裁剪结果当作所有 NLS 会话都一样。
新分区写入量大时,过期统计会影响行数估算。STALE_STATS=NO 也不是“行数绝对准确”,它反映数据库对统计过期的判断;要结合最近装载量和真实计划行数看。
分区级列统计帮助估算分区内过滤条件的选择率。看到直方图不代表计划就一定好;要看估计行数与实际行数偏差,以及 SQL 是只按日期过滤还是还按其他列过滤。
表级总行数与各分区统计不一致时,先确认最近是否装载、交换或删除分区。GLOBAL_STATS 告诉你统计是收集或增量维护的状态,不能只凭表级 LAST_ANALYZED 判断新分区已经正确统计。
这些是上次统计收集后的近似修改计数,能指出最近装载集中在哪个分区,但不能用作精确业务行数。新分区数据刚入库时,结合第 18 条的 STALE_STATS 判断先收集哪一层统计。
只指定目标分区,适合每月装载后的补统计。执行前确认装载已经结束;复合分区表还要检查子分区统计。收集完回看第 18、20 条和目标 SQL 的游标计划,不能只靠过程返回成功判断计划已改善。
维护表分区前先确定有哪些局部、全局分区索引。LOCALITY 说明是否局部索引,ALIGNMENT 说明索引键是否以分区键开头,不能把两列混为一个“是否与表分区对齐”的结论。
复合分区表不能只检查索引分区顶层状态。若只看到某个子分区 UNUSABLE,先确认它对应的表子分区以及最近的交换、拆分操作,再决定是重建单个子分区还是整条索引。
这条列的是 ORDERS 自身的约束。交换或删除分区前还要查别的表是否通过外键引用它;只看分区表上的 R 约束,会漏掉被引用方。
DROP、TRUNCATE 或交换分区若会影响这些子表,要先确认约束状态和业务数据关系。外键定义存在不代表当前已启用,STATUS 和 VALIDATED 要一起看。
增加或拆分分区时,先确认目标分区是否已经存在,避免把同名错误只当作语法问题。复合分区的顶层 SEGMENT_CREATED 表示对子分区的默认属性,不表示整个分区已分配段;实际占用仍查子分区和段视图。
下面的 ADD PARTITION 只适用于手工维护的 RANGE 示例。INTERVAL 表会在相应范围首次写入时自动创建分区,不能直接套用这条 ADD;复合分区还要按子分区模板或定义补全语法。
新增边界必须高于现有最后一个分区。若已经有 MAXVALUE 分区,就拆分那个分区,不能再往它后面 ADD。DDL 会提交,先确认表空间、分区键和维护窗口。
局部索引会随表分区增加对应分区,但系统产生的索引分区名不一定与表分区同名。这里用 PARTITION_POSITION 对照,不靠名字猜。若没有局部索引,这条没有结果;表分区本身仍需用第 27 条的视图核对。
拆分点要落在 P_FUTURE 的边界之内。分区里有数据时,拆分可能搬行并更新索引,时间与空间成本不能按空分区估算。UPDATE INDEXES 用于避免相关索引变为不可用,执行后仍要实查状态。
HIGH_VALUE 是 LONG,这里只直接显示原始表达式;不要用普通字符串函数切它。新分区的上界应是 2026-11-01,未来分区继续覆盖剩余范围,然后再检查两边的索引状态和数据。
这项语法复制列顺序和列属性,比 CREATE TABLE AS SELECT 更适合做交换表。它不会复制索引、统计设置或业务数据;若原表是复合分区,交换表还必须匹配目标子分区布局,这个简单示例不能直接套用。
要交换到 P2026_09,装载行必须符合它的完整边界。这里假设分区键只有 ORDER_DATE 且该分区覆盖整个 2026 年 9 月;边界若不是月初月末,必须改条件。WITH VALIDATION 还会由数据库核对,手工查询不能取代它。
交换是两边数据段互换,不是把交换表的行追加到原分区。如果目标分区已有业务数据,交换后旧数据会到 ORDERS_STG;先确认这是不是迁移方案预期,并保存交换前的数量。
WITH VALIDATION 会拒绝不属于目标分区的行。UPDATE INDEXES 在交换操作中只维护分区表的全局索引,不会修复局部索引。这张交换表没有建立匹配索引,默认 EXCLUDING INDEXES 会让对应局部索引分区不可用;交换后要按实际索引分区名重建。若表有主键、唯一键或引用约束,先按实际结构确认是否允许交换。
把结果与第 35 条倒过来对照。若交换表原先有行而目标分区也有行,两边数量应互换;同时查关键订单号与索引状态。只看到 DDL 执行成功,不足以证明业务读的是预期数据。
交换后先用真实索引分区名定位 UNUSABLE,不要把表分区名直接当索引分区名。全局索引另查第 12 条的查询;两种索引的修复方式不同。
重建会读取目标表分区,并占用 I/O 与空间,不能在业务高峰把它当作无成本的收尾步骤。完成后再查 DBA_IND_PARTITIONS.STATUS;若表是复合分区,可能需要重建的是索引子分区而非顶层分区。
域索引会限制某些一次处理多分区的 DDL,也可能需要专门维护。查到结果时先看具体索引实现,不要把普通 B-tree 索引的处理方式照搬到它身上。
删除分区会连数据段一起去掉;要归档就先做交换、备份并验证归档数据,而不是只把行数记在操作单上。COUNT(*) 是本次清理的一个基线,关键订单号仍需在归档表回读。
DROP 删除分区元数据和数据,不进入回收站。UPDATE INDEXES 让全局索引保持可用;在符合条件的堆表上,19c 可以使用异步全局索引维护。DDL 后仍要查分区清单、全局索引状态和 orphaned entries。
ORPHANED_ENTRIES=YES 可出现在分区删除或清空后的异步索引维护中;索引仍可为 VALID,不等于要立即重建。分区全局索引另查对应索引分区状态,不能只看这张非分区索引清单。
TRUNCATE PARTITION 会移除该分区的所有行,但分区定义还在,局部索引对应分区也随之清空。它不是按条件删除,不可用普通事务回滚;要先核对引用约束和数据恢复路径。
修改普通分区属性不能直接改现有段的 TABLESPACE,搬已有数据要用 MOVE PARTITION。搬迁会占用额外空间与 I/O;UPDATE INDEXES 维护受影响索引,作业后仍检查实际表空间与索引可用性。
表空间名应是 ORDERS_ARCH_TS;如果复合分区的实际段在子分区上,还要查子分区视图。搬完数据也不能只核对表段,局部索引分区和全局索引状态都要回看。
复合分区的表数据段通常在子分区层级。第 46 条只显示上层分区属性时,不能据此断定每个实际段都搬到了目标表空间;这里按子分区逐个核对。表名换成现场的复合分区表。
复合分区表的局部索引状态要下探到子分区。执行 MOVE SUBPARTITION 或分区交换后,局部索引子分区可能不可用;先定位准确名称,再决定是重建该子分区,还是在原维护语句中保留索引。
复合分区表的局部索引不能一次 REBUILD 整个索引,需处理准确的索引子分区。重建会扫描相应表子分区并消耗空间;执行后再查第 48 条,确认状态已回到 USABLE。
非空子分区搬迁后,若不维护索引,对应局部索引子分区可能变成 UNUSABLE。UPDATE INDEXES 让本次适用的索引维护跟着 DDL 做;表含 LOB 列时,LOB 段要单独按实际存储子句核对,不能只看表段已搬好。
表空间应为 ORDERS_ARCH_TS。若子分区段尚未创建,字典中的表空间属性不能证明有实际数据段已搬迁;再结合第 47 条的全量清单和局部索引子分区状态回看。
新分区装载后只收集该分区统计,表级全局统计不一定就跟上。INCREMENTAL=TRUE 允许用分区 synopsis 维护全局统计,但还需 PUBLISH=TRUE,收集时使用自动采样和 GRANULARITY=AUTO 等条件;先看实际偏好值,再决定统计作业怎么跑。
统计被锁定时,自动统计作业不会按平常方式刷新它。遇到分区明明装了新数据、计划却仍按旧行数估算,先看 STATTYPE_LOCKED,不要直接重复跑收集命令。
增量统计允许通过各分区的 synopsis 更新全局统计,适合经常新增和交换分区的大表。它不表示设置完成后全局统计马上变新;还要执行收集并核对统计发布时间。
分区 DML 后是否重建 synopsis,受 INCREMENTAL_STALENESS 和统计过期比例影响。现场若改过偏好,先读实际值,再解释为什么这次只刷新某些分区;不要把 STALE_STATS=NO 当成每列估算都正确。
第 22 条也是单分区统计,但这里明确给出增量全局统计所需的自动采样与粒度。收集后还要查看分区及全局统计的 LAST_ANALYZED;若表级统计没更新,不能只凭过程正常返回就说优化器已看到新数据。
NUM_ROWS 是统计估计值,不是实时计数。若新分区统计已刷新而表级仍显示旧时间和旧行数,继续检查增量统计条件、synopsis 和统计锁;必要时安排表级收集。
FAILED 和 TIMED OUT 值得先看 NOTES 与自动任务窗口。此视图记录 schema/database 级 DBMS_STATS 操作,不能保证单分区手工收集都在这里;结果仍要回到第 57 条检查对象统计时间。
RANGE 分区合并要求边界相邻;位置不连续就不能直接合。HIGH_VALUE 是 LONG,这里只把它显示出来人工核对,不做字符串截取或比较。还要确认两边是否有数据及目标表空间余量。
这是实际扫描,不是统计行数,历史大分区执行前要算 I/O 和时间。两份基线之和用于合并后验收;业务关键键仍要另做抽查。
局部索引分区和全局分区索引的处理方式不同。先拿到操作前状态,后面出现 UNUSABLE 才能分辨是本次合并导致,还是原本就坏。
合并后原两个分区会消失,新分区继承较高的上边界。UPDATE INDEXES 会随 DDL 维护适用索引,耗时和额外空间要按现场表大小估算;没写这项时,局部与全局索引可能被标记不可用。
看新分区是否存在,并与第 59 条的高边界对照。SEGMENT_CREATED=NO 表示还没建物理段,不能仅凭字典中的表空间名说数据已迁完。
这里应与第 60 条两边行数之和一致。行数是必要的初验,但不足以证明每个订单都正确;出现差异应停止后续清理,先看 DDL 日志、并发写入和抽样键。
这一条和第 61 条使用同一视图,但作为操作后回读必须重新执行。新局部索引分区名和旧分区不一定能按字符串推断;全局索引也要检查可用性,不能只看合并 DDL 执行成功。
HASH 分区不能像 RANGE 那样指定两个分区 MERGE。缩减分区数用 COALESCE PARTITION,数据库决定撤销哪一个并把行重新分布到其余分区。复合分区表还要分清顶层和子层是否为 HASH。
PARTITION_COUNT 是配置线索,实际名称和段位置以字典清单为准。NUM_ROWS 为统计估计值,不够新时要另算实际行数;缩容前至少保留原分区数量和表空间布局。
HASH 缩容会重分布行,无法只用被撤销的单个分区行数判断有没有丢数据。全表计数作为操作前基线,后面还要按业务键抽样。
这会把一个 HASH 分区的行分散到其余分区,并删除被选中的分区;不能指定要删哪一个。UPDATE INDEXES 可能延长 DDL 用时,但能避免适用索引在维护后不可用。表剩一个分区时不能继续这样缩。
与第 67 条比较,应该少一个实际分区。别预先写死“删除 P8”:HASH 由数据库选择和重分布;新分区的空间位置也要核对。
应与第 68 条的基线相同,前提是操作窗口没有并发写入。即使行数相同,也要抽查业务键并检查索引状态,不能只报“COALESCE 成功”。
若 SUBPARTITIONING_TYPE='HASH',才考虑对某个顶层分区执行子分区缩容。别把第 69 条的顶层 COALESCE PARTITION 套到 RANGE-HASH 复合表上。
子分区缩容只影响指定顶层分区。先数清当前有几份,确认每份表空间容量;如果是区间复合分区,还要确认这一层已经物化。
数据库会在 P2026_09 内选择一个 HASH 子分区并重分布其行。它不影响其他顶层月份,但该月的数据和索引会经历维护;执行后仍要查子分区清单和索引子分区状态。
不是所有局部索引子分区都一定和表子分区同名,所以按表找全量索引子分区,再对照第 73 条的变更前清单。若有 UNUSABLE,定位准确名称后处理,别直接重建整个索引。
先用第 1 条的分区方式确认是 LIST,再人工读 HIGH_VALUE 中的地区值。它是 LONG,不能随便写 LIKE 搜索;待拆值必须确实属于旧分区,避免窗口里才发现值不匹配。
这几种地区码要按现场值替换。旧分区里若还存着其他地区,拆分后它们会进另一份分区;统计基线能帮我们核对分布,但仍要看旧分区总量。
指定值的行进入 REGION_NORTHEAST,剩余值进入 REGION_EAST_REST,原分区名消失。DDL 会移动行并维护索引;分区里数据多时,不能把它当作瞬间改名。
两份新分区都应出现,边界值要和第 76 条的旧值清单对得上。表空间属性不是实际占用;有数据的分区还要结合段和行数核对。
两份之和应等于拆分前旧分区行数,在无并发写入的维护窗口才好直接比较。再抽查 CT、MA、MD 是否只落在新分区;如果索引不可用,也要先处理再交付业务。
普通分区的压缩属性和复合分区上层的默认属性不是一回事。这里以单层 RANGE 表为例;若实际数据在子分区段,改查 DBA_TAB_SUBPARTITIONS。
在原本全不压缩的分区表上第一次引入压缩时,可用 BITMAP 索引可能阻止操作。先查清索引并制定重建方案;不能看到“只有一个老分区要压缩”就忽略全表的 BITMAP 索引。
分配空间并非表行实际占用空间,却能作为搬迁前后的粗略对照。压缩操作可能需要额外工作空间,不能拿现有分区大小当成目标表空间只需同样余量。
已有分区统计可能过期,所以维护前按现场允许的 I/O 窗口做实际计数。若业务仍并发写入这个历史分区,操作前后总数不能简单相减验收。
只修改压缩属性不等于把已有行重新压缩;MOVE PARTITION ... COMPRESS 会搬现有数据。DDL 消耗 I/O 和额外空间,UPDATE INDEXES 维护适用索引;首次压缩涉及 BITMAP 索引时,先按第 82 条制定停用与重建步骤。
与第 81 条对照,确认压缩已启用、表空间也已更换。字典属性还不能证明实际节省了多少空间,要再查段大小;含 LOB 列时另核对 LOB 段。
与第 83 条对比;若大小没怎么降,先看压缩方式、行模式和段分配情况,不要立刻再搬一次。段大小减少也不意味着查询一定变快,真实计划和 I/O 仍要看。
应与第 84 条基线一致,条件仍是窗口内没有并发写入。对账不能只看一张表的行数;关键订单号、金额和子表关联也要抽样。
索引分区的表空间不一定跟表分区一起变化。重点看 STATUS 是否为 USABLE,再核对索引段位置;UPDATE INDEXES 成功返回后也应做这一步。
删除或清空分区后,全局索引可能保留指向旧段的条目,查询正确性不受影响,空间和效率却可能受影响。这个过程会做索引维护;Oracle 文档说明它可能忽略清理中遇到的错误,过程返回后仍要核对第 43 条的 ORPHANED_ENTRIES 和索引状态。
INTERVAL 非空时,后续新日期可能由写入自动建分区。应用原本每天手动 ADD PARTITION 的任务不能不改就继续跑;转换前先找自动建分区脚本和日期边界。
分区列的 INTERVAL='YES' 指它位于自动区间段,NO 指它位于原 RANGE 段。HIGH_VALUE 仍是 LONG,直接显示即可;字典里没出现将来某个月份,不等于那个月份一定会报错,自动分区通常要有新数据才物化。
转换后的自动区间从最高 RANGE 边界之后开始。若这个上界不是整月起点,按月的 interval 可能生成与你期望不一致的边界;先把表达式和业务日期口径对齐。
第 95 条用 NUMTOYMINTERVAL 表示月。先确认分区键是适用的日期类型,并检查现有最高边界;不能把这项设置套到按数字或非月度业务键分区的表上。
之后超出最高 RANGE 边界的写入可自动产生月分区。它不补齐历史数据、不自动重写应用维护脚本;存储位置和局部索引新分区仍要在测试库插入新月份样本后检查。
表上有 interval 表达式,不代表目标未来月份分区已经物化。回读当前范围后,再在测试环境验证新月份行的路由与表空间;不能对生产库随手插一条假订单做验证。
Oracle 会把已存在的 interval 分区转成 RANGE 分区。此后新高边界外的写入不会再自动建分区,应用可能碰到没有目标分区的错误;执行前应把接下来几个月的手工建分区计划接上。
这是第 97 条后的回读,不能和第 96 条混在一个执行序列里。确认表级 INTERVAL 为空,物化分区已转为 RANGE,再检查最高上界与下一次装载日期。
改名不改变分区边界,也不搬数据。上线脚本如果按旧 SYS_P... 名称引用分区,要一起改;局部索引分区名是否联动,不能靠猜,操作后查字典。
新名字要在表分区层面存在,索引分区有无对应名称与可用状态也要查。若索引分区没有同名结果,按第 75 条的方法查该表全量索引分区,别直接把空结果当成索引坏。
分区表上的慢 SQL,先看分区键和真实计划;做分区 DDL,先算清边界、行数和索引。RANGE、LIST、HASH、INTERVAL 的维护动作各不相同,不能把“都是分区”当成同一套操作。搬、拆、合、清理之后,表分区、索引分区和业务行数都要回读。
ORA100 DBA100 系列海报
更多数据库场景命令放在 ORA100 · DBA100:
微信里搜索小程序 「三笠的百令册」,也可以随时查。
1SELECT owner, table_name, partitioning_type,2 subpartitioning_type, partition_count3FROM dba_part_tables4WHERE owner = 'SALES' AND table_name = 'ORDERS';1SELECT column_name, column_position2FROM dba_part_key_columns3WHERE owner = 'SALES'4AND name = 'ORDERS'5AND object_type = 'TABLE'6ORDER BY column_position;1SELECT partition_name, partition_position, tablespace_name,2 num_rows, last_analyzed3FROM dba_tab_partitions4WHERE table_owner = 'SALES' AND table_name = 'ORDERS'5ORDER BY partition_position;1SELECT owner, index_name, locality, alignment, partitioning_type2FROM dba_part_indexes3WHERE owner = 'SALES' AND table_name = 'ORDERS'4ORDER BY index_name;1SELECT partition_name, subpartition_name, tablespace_name2FROM dba_tab_subpartitions3WHERE table_owner = 'SALES'4AND table_name = 'ORDERS'5AND partition_name = 'P2026_09'6ORDER BY subpartition_position;1SELECT column_name, column_position2FROM dba_subpart_key_columns3WHERE owner = 'SALES'4AND name = 'ORDERS'5AND object_type = 'TABLE'6ORDER BY column_position;1SELECT partition_name, high_value2FROM dba_tab_partitions3WHERE table_owner = 'SALES'4AND table_name = 'ORDERS'5AND partition_name = 'P2026_09';1SELECT partitioning_type, interval, partition_count2FROM dba_part_tables3WHERE owner = 'SALES' AND table_name = 'ORDERS';1SELECT partition_name, segment_type,2 ROUND(bytes/1024/1024, 1) allocated_mb3FROM dba_segments4WHERE owner = 'SALES'5AND segment_name = 'ORDERS'6AND segment_type IN ('TABLE PARTITION', 'TABLE SUBPARTITION')7ORDER BY allocated_mb DESC;1SELECT partition_name, segment_created2FROM dba_tab_partitions3WHERE table_owner = 'SALES' AND table_name = 'ORDERS'4ORDER BY partition_position;1SELECT p.index_name, p.partition_name, p.status2FROM dba_ind_partitions p3JOIN dba_part_indexes i4 ON i.owner = p.index_owner AND i.index_name = p.index_name5WHERE i.owner = 'SALES' AND i.table_name = 'ORDERS'6AND i.locality = 'LOCAL'7ORDER BY p.index_name, p.partition_position;1SELECT p.index_name, p.partition_name, p.status,2 p.orphaned_entries3FROM dba_ind_partitions p4JOIN dba_part_indexes i5 ON i.owner = p.index_owner AND i.index_name = p.index_name6WHERE i.owner = 'SALES' AND i.table_name = 'ORDERS'7AND i.locality = 'GLOBAL'8ORDER BY p.index_name, p.partition_position;1EXPLAIN PLAN FOR2SELECT COUNT(*)3FROM sales.orders4WHERE order_date >= DATE '2026-09-01'5AND order_date < DATE '2026-10-01';67SELECT * FROM TABLE(dbms_xplan.display(NULL, NULL, 'BASIC +PARTITION'));1EXPLAIN PLAN FOR2SELECT COUNT(*)3FROM sales.orders4WHERE TRUNC(order_date) = DATE '2026-09-01';56SELECT * FROM TABLE(dbms_xplan.display(NULL, NULL, 'BASIC +PARTITION +PREDICATE'));1-- SQL_ID 换成业务语句的值;child_number 按实际游标选择2SELECT * FROM TABLE(3 dbms_xplan.display_cursor('9abcde12345fg', 0,4 'TYPICAL +PARTITION +PREDICATE ALLSTATS LAST'));1SELECT COUNT(*)2FROM sales.orders PARTITION (P2026_09);1SELECT c.column_name, c.data_type2FROM dba_tab_columns c3JOIN dba_part_key_columns k4 ON k.owner = c.owner AND k.name = c.table_name5 AND k.column_name = c.column_name6WHERE k.owner = 'SALES'7AND k.name = 'ORDERS'8AND k.object_type = 'TABLE'9ORDER BY k.column_position;1SELECT partition_name, num_rows, last_analyzed, stale_stats2FROM dba_tab_statistics3WHERE owner = 'SALES'4AND table_name = 'ORDERS'5AND object_type = 'PARTITION'6ORDER BY partition_name;1SELECT partition_name, column_name, num_distinct,2 histogram, last_analyzed3FROM dba_part_col_statistics4WHERE owner = 'SALES'5AND table_name = 'ORDERS'6AND partition_name = 'P2026_09'7AND column_name = 'ORDER_DATE';1SELECT object_type, partition_name, num_rows,2 last_analyzed, global_stats3FROM dba_tab_statistics4WHERE owner = 'SALES'5AND table_name = 'ORDERS'6AND object_type IN ('TABLE', 'PARTITION')7ORDER BY object_type, partition_name;1SELECT partition_name, subpartition_name,2 inserts, updates, deletes, truncated3FROM dba_tab_modifications4WHERE table_owner = 'SALES'5AND table_name = 'ORDERS'6ORDER BY partition_name, subpartition_name;1BEGIN2 dbms_stats.gather_table_stats(3 ownname => 'SALES',4 tabname => 'ORDERS',5 partname => 'P2026_09',6 granularity => 'PARTITION',7 cascade => dbms_stats.auto_cascade);8END;9/1SELECT i.index_name, p.locality, p.alignment,2 p.partitioning_type, p.partition_count3FROM dba_indexes i4JOIN dba_part_indexes p5 ON p.owner = i.owner AND p.index_name = i.index_name6WHERE i.table_owner = 'SALES'7AND i.table_name = 'ORDERS'8ORDER BY i.index_name;1SELECT s.index_owner, s.index_name,2 s.partition_name, s.subpartition_name, s.status3FROM dba_ind_subpartitions s4JOIN dba_indexes i5 ON i.owner = s.index_owner AND i.index_name = s.index_name6WHERE i.table_owner = 'SALES'7AND i.table_name = 'ORDERS'8AND s.status <> 'USABLE'9ORDER BY s.index_name, s.partition_name, s.subpartition_name;1SELECT c.owner, c.table_name, c.constraint_name,2 c.constraint_type, c.status, c.validated3FROM dba_constraints c4WHERE c.owner = 'SALES'5AND c.table_name = 'ORDERS'6ORDER BY c.constraint_type, c.constraint_name;1SELECT child.owner, child.table_name,2 child.constraint_name, child.status, child.validated3FROM dba_constraints child4JOIN dba_constraints parent5 ON parent.owner = child.r_owner6 AND parent.constraint_name = child.r_constraint_name7WHERE child.constraint_type = 'R'8AND parent.owner = 'SALES'9AND parent.table_name = 'ORDERS'10ORDER BY child.owner, child.table_name;1SELECT partition_name, partition_position,2 tablespace_name, segment_created3FROM dba_tab_partitions4WHERE table_owner = 'SALES'5AND table_name = 'ORDERS'6AND partition_name = 'P2026_09';1SELECT owner, table_name, partitioning_type,2 interval, subpartitioning_type3FROM dba_part_tables4WHERE owner = 'SALES'5AND table_name = 'ORDERS';1-- 仅当 ORDERS 是单层 RANGE,最高边界小于 2026-11-01,且没有 MAXVALUE 分区2ALTER TABLE sales.orders3 ADD PARTITION p2026_104 VALUES LESS THAN (DATE '2026-11-01')5 TABLESPACE orders_ts;1SELECT t.partition_name, t.partition_position,2 ix.index_name, ip.partition_name index_partition,3 ip.status index_status4FROM dba_tab_partitions t5JOIN dba_indexes ix6 ON ix.table_owner = t.table_owner7 AND ix.table_name = t.table_name8JOIN dba_part_indexes px9 ON px.owner = ix.owner AND px.index_name = ix.index_name10 AND px.locality = 'LOCAL'11JOIN dba_ind_partitions ip12 ON ip.index_owner = ix.owner AND ip.index_name = ix.index_name13 AND ip.partition_position = t.partition_position14WHERE t.table_owner = 'SALES'15AND t.table_name = 'ORDERS'16AND t.partition_name = 'P2026_10';1-- 独立示例:已有 P_FUTURE(MAXVALUE),不是第 29 条 ADD 后续步骤2ALTER TABLE sales.orders3 SPLIT PARTITION p_future AT (DATE '2026-11-01')4 INTO (PARTITION p2026_10 TABLESPACE orders_ts,5 PARTITION p_future)6 UPDATE INDEXES;1SELECT partition_name, partition_position, high_value2FROM dba_tab_partitions3WHERE table_owner = 'SALES'4AND table_name = 'ORDERS'5AND partition_name IN ('P2026_10', 'P_FUTURE')6ORDER BY partition_position;1-- 单层 RANGE 分区示例;在装载前创建空的非分区交换表2CREATE TABLE sales.orders_stg3 FOR EXCHANGE WITH TABLE sales.orders;1SELECT COUNT(*) out_of_range_rows2FROM sales.orders_stg3WHERE order_date < DATE '2026-09-01'4OR order_date >= DATE '2026-10-01'5OR order_date IS NULL;1SELECT 'P2026_09' location, COUNT(*) rows_count2FROM sales.orders PARTITION (p2026_09)3UNION ALL4SELECT 'ORDERS_STG', COUNT(*)5FROM sales.orders_stg;1-- 已核对约束、交换表结构、9 月边界和全局索引;在维护窗口执行2ALTER TABLE sales.orders3 EXCHANGE PARTITION p2026_094 WITH TABLE sales.orders_stg5 WITH VALIDATION6 UPDATE INDEXES;1SELECT 'P2026_09' location, COUNT(*) rows_count2FROM sales.orders PARTITION (p2026_09)3UNION ALL4SELECT 'ORDERS_STG', COUNT(*)5FROM sales.orders_stg;1SELECT ix.owner, ix.index_name,2 ip.partition_name index_partition, ip.status3FROM dba_indexes ix4JOIN dba_part_indexes px5 ON px.owner = ix.owner AND px.index_name = ix.index_name6 AND px.locality = 'LOCAL'7JOIN dba_ind_partitions ip8 ON ip.index_owner = ix.owner AND ip.index_name = ix.index_name9JOIN dba_tab_partitions tp10 ON tp.table_owner = ix.table_owner11 AND tp.table_name = ix.table_name12 AND tp.partition_position = ip.partition_position13WHERE ix.table_owner = 'SALES'14AND ix.table_name = 'ORDERS'15AND tp.partition_name = 'P2026_09'16AND ip.status <> 'USABLE';1-- 示例索引名与分区名必须先从第 38 条结果核对2ALTER INDEX sales.orders_lix REBUILD PARTITION orders_lix_p09;1SELECT owner, index_name, index_type, domidx_status2FROM dba_indexes3WHERE table_owner = 'SALES'4AND table_name = 'ORDERS'5AND index_type IN ('DOMAIN', 'FUNCTION-BASED DOMAIN');1-- 示例旧分区 P2025_01;大分区的 COUNT(*) 会产生实际读取2SELECT COUNT(*) old_rows3FROM sales.orders PARTITION (p2025_01);1-- 仅在旧数据已归档并回读、业务确认不再读取该分区后执行2ALTER TABLE sales.orders3 DROP PARTITION p2025_014 UPDATE INDEXES;1SELECT owner, index_name, status, orphaned_entries2FROM dba_indexes3WHERE table_owner = 'SALES'4AND table_name = 'ORDERS'5AND partitioned = 'NO'6AND orphaned_entries = 'YES';1-- 独立示例:仅用于允许清空的 P2026_01;执行前保留可恢复备份2ALTER TABLE sales.orders3 TRUNCATE PARTITION p2026_014 UPDATE INDEXES;1-- 单层分区示例;确认 ORDERS_ARCH_TS 容量与维护窗口2ALTER TABLE sales.orders3 MOVE PARTITION p2025_124 TABLESPACE orders_arch_ts5 UPDATE INDEXES;1SELECT partition_name, tablespace_name,2 segment_created, last_analyzed3FROM dba_tab_partitions4WHERE table_owner = 'SALES'5AND table_name = 'ORDERS'6AND partition_name = 'P2025_12';1SELECT table_owner, table_name, partition_name,2 subpartition_name, tablespace_name3FROM dba_tab_subpartitions4WHERE table_owner = 'SALES'5AND table_name = 'ORDERS_COMP'6ORDER BY partition_position, subpartition_position;1SELECT index_owner, index_name, partition_name,2 subpartition_name, status3FROM dba_ind_subpartitions4WHERE index_owner = 'SALES'5AND status = 'UNUSABLE'6ORDER BY index_name, partition_name, subpartition_name;1-- 先从第 48 条核对真实索引名、子分区名及目标表空间配额2ALTER INDEX sales.orders_comp_lix3 REBUILD SUBPARTITION orders_comp_lix_p09_east;1-- 独立示例:确认子分区名、目标表空间容量及索引维护窗口2ALTER TABLE sales.orders_comp3 MOVE SUBPARTITION orders_p2025_12_east4 TABLESPACE orders_arch_ts5 UPDATE INDEXES;1SELECT subpartition_name, tablespace_name,2 segment_created, last_analyzed3FROM dba_tab_subpartitions4WHERE table_owner = 'SALES'5AND table_name = 'ORDERS_COMP'6AND subpartition_name = 'ORDERS_P2025_12_EAST';1SELECT dbms_stats.get_prefs('INCREMENTAL', 'SALES', 'ORDERS')2 AS incremental_pref,3 dbms_stats.get_prefs('PUBLISH', 'SALES', 'ORDERS')4 AS publish_pref,5 dbms_stats.get_prefs('GRANULARITY', 'SALES', 'ORDERS')6 AS granularity_pref7FROM dual;1SELECT object_type, partition_name, stattype_locked,2 last_analyzed, stale_stats3FROM dba_tab_statistics4WHERE owner = 'SALES'5AND table_name = 'ORDERS'6ORDER BY object_type, partition_name;1-- 在测试和上线方案确认后设置;会增加 synopsis 存储开销2BEGIN3 dbms_stats.set_table_prefs(4 ownname => 'SALES', tabname => 'ORDERS',5 pname => 'INCREMENTAL', pvalue => 'TRUE');6END;7/1SELECT dbms_stats.get_prefs('INCREMENTAL_STALENESS',2 'SALES', 'ORDERS') AS incremental_staleness,3 dbms_stats.get_prefs('STALE_PERCENT',4 'SALES', 'ORDERS') AS stale_percent5FROM dual;1-- P2026_09 已装载并完成验收;避开高峰期2BEGIN3 dbms_stats.gather_table_stats(4 ownname => 'SALES', tabname => 'ORDERS',5 partname => 'P2026_09',6 estimate_percent => dbms_stats.auto_sample_size,7 granularity => 'AUTO', cascade => TRUE);8END;9/1SELECT object_type, partition_name,2 num_rows, blocks, last_analyzed3FROM dba_tab_statistics4WHERE owner = 'SALES'5AND table_name = 'ORDERS'6AND (partition_name IS NULL OR partition_name = 'P2026_09')7ORDER BY object_type;1SELECT operation, target, start_time, end_time,2 status, notes3FROM dba_optstat_operations4WHERE start_time >= SYSTIMESTAMP - INTERVAL '1' DAY5ORDER BY start_time DESC;1SELECT partition_name, partition_position,2 high_value, tablespace_name3FROM dba_tab_partitions4WHERE table_owner = 'SALES'5AND table_name = 'ORDERS'6AND partition_name IN ('P2025_11', 'P2025_12')7ORDER BY partition_position;1SELECT 'P2025_11' AS partition_name, COUNT(*) AS rows_count2FROM sales.orders PARTITION (P2025_11)3UNION ALL4SELECT 'P2025_12', COUNT(*)5FROM sales.orders PARTITION (P2025_12);1SELECT index_owner, index_name, partition_name, status2FROM dba_ind_partitions3WHERE index_owner = 'SALES'4AND index_name IN ('ORDERS_LIX', 'ORDERS_GIX')5ORDER BY index_name, partition_position;1-- 独立示例;确认表为 RANGE、分区相邻、已备份并安排维护窗口2ALTER TABLE sales.orders3 MERGE PARTITIONS p2025_11, p2025_124 INTO PARTITION p2025_nov_dec5 UPDATE INDEXES;1SELECT partition_name, partition_position,2 high_value, tablespace_name, segment_created3FROM dba_tab_partitions4WHERE table_owner = 'SALES'5AND table_name = 'ORDERS'6AND partition_name = 'P2025_NOV_DEC';1SELECT COUNT(*) AS rows_count2FROM sales.orders PARTITION (P2025_NOV_DEC);1SELECT index_owner, index_name, partition_name, status2FROM dba_ind_partitions3WHERE index_owner = 'SALES'4AND index_name IN ('ORDERS_LIX', 'ORDERS_GIX')5ORDER BY index_name, partition_position;1SELECT owner, table_name, partitioning_type,2 subpartitioning_type, partition_count3FROM dba_part_tables4WHERE owner = 'SALES'5AND table_name = 'ORDER_EVENTS_HASH';1SELECT partition_name, partition_position,2 num_rows, tablespace_name3FROM dba_tab_partitions4WHERE table_owner = 'SALES'5AND table_name = 'ORDER_EVENTS_HASH'6ORDER BY partition_position;1-- 大表需评估全表扫描,安排低峰窗口2SELECT COUNT(*) AS before_rows3FROM sales.order_events_hash;1-- 独立示例;确认容量、索引维护和回退方案2ALTER TABLE sales.order_events_hash3 COALESCE PARTITION4 UPDATE INDEXES;1SELECT partition_name, partition_position,2 tablespace_name3FROM dba_tab_partitions4WHERE table_owner = 'SALES'5AND table_name = 'ORDER_EVENTS_HASH'6ORDER BY partition_position;1SELECT COUNT(*) AS after_rows2FROM sales.order_events_hash;1SELECT partitioning_type, subpartitioning_type,2 partition_count3FROM dba_part_tables4WHERE owner = 'SALES'5AND table_name = 'ORDERS_COMP_HASH';1SELECT partition_name, subpartition_name,2 subpartition_position, tablespace_name3FROM dba_tab_subpartitions4WHERE table_owner = 'SALES'5AND table_name = 'ORDERS_COMP_HASH'6AND partition_name = 'P2026_09'7ORDER BY subpartition_position;1-- 独立示例;第 72—73 条已确认类型、数量和空间2ALTER TABLE sales.orders_comp_hash3 MODIFY PARTITION p2026_094 COALESCE SUBPARTITION5 UPDATE INDEXES;1SELECT sp.index_name, sp.partition_name,2 sp.subpartition_name, sp.status3FROM dba_ind_subpartitions sp4JOIN dba_indexes i5 ON i.owner = sp.index_owner6 AND i.index_name = sp.index_name7WHERE i.table_owner = 'SALES'8AND i.table_name = 'ORDERS_COMP_HASH'9ORDER BY sp.index_name, sp.partition_name,10 sp.subpartition_position;1SELECT partition_name, partition_position, high_value2FROM dba_tab_partitions3WHERE table_owner = 'SALES'4AND table_name = 'ORDERS_REGION'5ORDER BY partition_position;1SELECT region_code, COUNT(*) AS rows_count2FROM sales.orders_region PARTITION (REGION_EAST)3WHERE region_code IN ('CT', 'MA', 'MD')4GROUP BY region_code5ORDER BY region_code;1-- 独立示例;确认地区值确属 REGION_EAST,已预估空间和索引维护2ALTER TABLE sales.orders_region3 SPLIT PARTITION region_east VALUES ('CT', 'MA', 'MD')4 INTO (PARTITION region_northeast,5 PARTITION region_east_rest)6 UPDATE INDEXES;1SELECT partition_name, high_value, tablespace_name2FROM dba_tab_partitions3WHERE table_owner = 'SALES'4AND table_name = 'ORDERS_REGION'5AND partition_name IN ('REGION_NORTHEAST', 'REGION_EAST_REST');1SELECT 'REGION_NORTHEAST' AS partition_name, COUNT(*) AS rows_count2FROM sales.orders_region PARTITION (REGION_NORTHEAST)3UNION ALL4SELECT 'REGION_EAST_REST', COUNT(*)5FROM sales.orders_region PARTITION (REGION_EAST_REST);1SELECT partition_name, compression, compress_for,2 tablespace_name, segment_created3FROM dba_tab_partitions4WHERE table_owner = 'SALES'5AND table_name = 'ORDERS'6AND partition_name = 'P2025_12';1SELECT owner, index_name, index_type, status2FROM dba_indexes3WHERE table_owner = 'SALES'4AND table_name = 'ORDERS'5AND index_type = 'BITMAP'6ORDER BY index_name;1SELECT segment_name, partition_name,2 ROUND(bytes / 1048576, 2) AS allocated_mb3FROM dba_segments4WHERE owner = 'SALES'5AND segment_name = 'ORDERS'6AND partition_name = 'P2025_12'7AND segment_type = 'TABLE PARTITION';1SELECT COUNT(*) AS before_rows2FROM sales.orders PARTITION (P2025_12);1-- 独立示例;确认表空间余量、BITMAP 索引和压缩许可2ALTER TABLE sales.orders3 MOVE PARTITION p2025_124 TABLESPACE orders_arch_ts5 COMPRESS6 UPDATE INDEXES;1SELECT partition_name, compression, compress_for,2 tablespace_name, segment_created3FROM dba_tab_partitions4WHERE table_owner = 'SALES'5AND table_name = 'ORDERS'6AND partition_name = 'P2025_12';1SELECT segment_name, partition_name,2 ROUND(bytes / 1048576, 2) AS allocated_mb3FROM dba_segments4WHERE owner = 'SALES'5AND segment_name = 'ORDERS'6AND partition_name = 'P2025_12'7AND segment_type = 'TABLE PARTITION';1SELECT COUNT(*) AS after_rows2FROM sales.orders PARTITION (P2025_12);1SELECT index_owner, index_name, partition_name,2 status, tablespace_name3FROM dba_ind_partitions4WHERE index_owner = 'SALES'5AND partition_name = 'P2025_12'6ORDER BY index_name;1-- 只在第 43 条已确认 orphaned entries 且有维护窗口时执行2BEGIN3 dbms_part.cleanup_gidx(4 schema_name_in => 'SALES',5 table_name_in => 'ORDERS');6END;7/1SELECT owner, table_name, partitioning_type,2 interval, partition_count3FROM dba_part_tables4WHERE owner = 'SALES'5AND table_name = 'ORDERS_MONTHLY';1SELECT partition_name, partition_position,2 interval, segment_created, high_value3FROM dba_tab_partitions4WHERE table_owner = 'SALES'5AND table_name = 'ORDERS_MONTHLY'6ORDER BY partition_position;1SELECT partition_name, partition_position,2 high_value3FROM dba_tab_partitions4WHERE table_owner = 'SALES'5AND table_name = 'ORDERS_MONTHLY'6AND interval = 'NO'7ORDER BY partition_position DESC8FETCH FIRST 1 ROW ONLY;1SELECT c.column_name, c.data_type,2 k.column_position3FROM dba_part_key_columns k4JOIN dba_tab_columns c5 ON c.owner = k.owner6 AND c.table_name = k.name7 AND c.column_name = k.column_name8WHERE k.owner = 'SALES'9AND k.name = 'ORDERS_MONTHLY'10AND k.object_type = 'TABLE'11ORDER BY k.column_position;1-- 独立示例;只在已检查最高边界和自动 ADD 脚本后执行2ALTER TABLE sales.orders_monthly3 SET INTERVAL (NUMTOYMINTERVAL(1, 'MONTH'));1SELECT t.interval AS interval_expression,2 p.partition_name, p.interval AS partition_section,3 p.tablespace_name4FROM dba_part_tables t5JOIN dba_tab_partitions p6 ON p.table_owner = t.owner7 AND p.table_name = t.table_name8WHERE t.owner = 'SALES'9AND t.table_name = 'ORDERS_MONTHLY'10ORDER BY p.partition_position;1-- 与第 95 条独立的反向示例;先准备手工 ADD PARTITION 方案2ALTER TABLE sales.orders_monthly3 SET INTERVAL ();1SELECT t.interval AS interval_expression,2 p.partition_name, p.interval AS partition_section3FROM dba_part_tables t4JOIN dba_tab_partitions p5 ON p.table_owner = t.owner6 AND p.table_name = t.table_name7WHERE t.owner = 'SALES'8AND t.table_name = 'ORDERS_MONTHLY'9ORDER BY p.partition_position;1-- 独立示例;SYS_P12345 必须先从第 92 条查真实名称2ALTER TABLE sales.orders_monthly3 RENAME PARTITION sys_p12345 TO p2026_10;1SELECT 'TABLE' AS object_type, partition_name, CAST(NULL AS VARCHAR2(8)) AS status2FROM dba_tab_partitions3WHERE table_owner = 'SALES'4AND table_name = 'ORDERS_MONTHLY'5AND partition_name = 'P2026_10'6UNION ALL7SELECT 'INDEX', p.partition_name, p.status8FROM dba_ind_partitions p9JOIN dba_indexes i10 ON i.owner = p.index_owner11 AND i.index_name = p.index_name12WHERE i.table_owner = 'SALES'13AND i.table_name = 'ORDERS_MONTHLY'14AND p.partition_name = 'P2026_10';