ALTER TABLE ... MOVE 后 ROWID 必然变化,因该操作重建段并重新分配所有物理地址;索引随之失效需手动重建,LOB段、统计信息等亦不自动继承。
alter table ... move 后 rowid 必然变化,这不是异常,是 oracle 对“物理重写”的明确承诺。
MOVE 操作本质是重建段,不是移动数据块
MOVE 不是把现有数据块从一个位置“搬”到另一个位置,而是创建一个全新的段(new segment),把原表所有行 INSERT 进去,再让表定义指向新段,最后释放旧段。这个过程和 CREATE TABLE AS SELECT + DROP + RENAME 逻辑等价。
由于新段的文件号、块号、行号全部重新分配,ROWID 的四个组成部分(OBJ#、RFILE#、BLOCK#、ROW#)全部刷新,所以每行的 ROWID 都不同。
- 即使表没分区、没启用
ENABLE ROW MOVEMENT,MOVE 也会变 ROWID - MOVE 不依赖行移动开关——它根本不管原行在哪,只管“重写”
- 执行后查
SELECT ROWID FROM t1,结果和 MOVE 前完全不重叠
索引失效不是 bug,是 ROWID 失效的必然结果
B*Tree 索引叶节点里存的是 KEY → ROWID 映射。MOVE 后所有旧 ROWID 全部作废,索引条目仍指向已删除的旧块地址,Oracle 只能将索引状态置为 UNUSABLE,拒绝使用。
-
SELECT可能仍走索引(如果 optimizer 判定统计信息未更新、且未触发检查),但实际执行会报ORA-01502 -
UPDATE/DELETE若用到该索引定位,直接失败 - 必须显式执行
ALTER INDEX idx_name REBUILD,否则无法恢复 -
LOB段、统计信息、约束状态(如CHECK)也都不会自动继承,需单独处理
Shrink space 和 Move 都变 ROWID,但机制和影响不同
SHRINK SPACE 也改变 ROWID,但它靠 DELETE + INSERT 在原段内重组数据;而 MOVE 是彻底换段。两者都要求先 ENABLE ROW MOVEMENT(仅对 shrink 的 compact 阶段强制,MOVE 不需要)。
- Shrink 不改段头块位置,MOVE 改——这对依赖
DBA_SEGMENTS中HEADER_BLOCK的监控脚本有影响 - Shrink 可分两步:
SHRINK SPACE COMPACT(加 RX 锁,业务影响小)+SHRINK SPACE(加 X 锁,阻塞 DML) - Move 期间全程加 X 锁,DML 完全阻塞,且需额外等于表大小的临时空间
- Shrink 后索引保持
VALID状态(除非加cascade),MOVE 后一律UNUSABLE
真正容易被忽略的点是:哪怕你只用 ROWID 做临时缓存(比如在 PL/SQL 循环里批量更新),只要中间穿插了 MOVE 或 SHRINK,缓存的 ROWID 就立刻失效。不要假设“刚查出来的 ROWID 下一秒还能用”,尤其在维护窗口执行过表重组之后。


















