ora-14402报错本质是oracle在dml前策略性拦截更新分区键的操作,前提是row movement未启用;启用后虽允许跨分区更新,但底层为“删旧插新”,必然导致rowid变更、全局索引失效及触发器双触发等隐性风险。

因为UPDATE分区键时默认禁止行迁移,底层不支持跨分区物理重定位,ROWID变更不是报错原因,而是启用ENABLE ROW MOVEMENT后的必然结果;真正触发报错的是ORA-14402——它发生在行迁移被禁用时,而非ROWID本身出问题。
ORA-14402报错的本质不是ROWID变不变
ORA-14402错误信息是updating partition key column would cause a partition change,它和ORA-01410: invalid rowid完全无关。前者是Oracle在DML执行前做的策略性拦截:只要UPDATE语句碰了分区键列(哪怕新旧值在同一分区),且表的row_movement为DISABLED,就直接拒绝,根本不走到物理更新那步。ROWID变化是开启ENABLE ROW MOVEMENT后才发生的副作用,不是报错源头。
为什么开了ENABLE ROW MOVEMENT后ROWID一定会变
开启ENABLE ROW MOVEMENT后,Oracle对跨分区UPDATE的实现方式就是“删旧插新”:
- 先按原ROWID在旧分区执行DELETE(释放原物理位置)
- 再把整行INSERT到目标分区(分配新数据块、新ROWID)
- 本地索引自动重建对应分区索引段,但全局索引条目仍指向旧ROWID,变成
UNUSABLE
这个过程天然导致ROWID变更——它不是bug,是设计使然。如果你的应用里硬编码依赖ROWID(比如某些触发器里写:OLD.ROWID做日志或比对),就会在迁移后读不到原记录,甚至触发ORA-01410。
哪些操作会悄悄让ROWID失效却不易察觉
除了显式UPDATE分区键,以下场景同样依赖ENABLE ROW MOVEMENT,且都会引发ROWID变更:
-
ALTER TABLE ... SHRINK SPACE:压缩高水位线时需移动行,没开该开关会报ORA-10636 -
FLASHBACK TABLE ... TO TIMESTAMP:闪回本质也是delete+insert,ROWID必然刷新 - 含域索引(Domain Index)的表:一旦建了域索引,
ENABLE ROW MOVEMENT就不可用,反过来也成立——两者互斥
这些操作不会报ORA-14402,但一旦row_movement关闭,它们会直接失败,而失败原因往往被误判为空间或权限问题。
查ROWID是否真变了?别只看SELECT,要盯住实际访问路径
单纯SELECT某行看到的ROWID变没变,意义不大。关键看下游是否真用到了它:
- 触发器里引用
:OLD.ROWID或:NEW.ROWID,迁移后:OLD.ROWID指向已删除行,执行SELECT * FROM t WHERE ROWID = :OLD.ROWID就会报ORA-01410 - 应用层缓存了ROWID做快速定位,迁移后缓存失效,查不到数据
- 全局索引未重建,
SELECT /*+ INDEX(t idx_global) */ ...可能返回陈旧或空结果
真正麻烦的从来不是“怎么让UPDATE跑通”,而是开了ENABLE ROW MOVEMENT之后,没人去核验触发器逻辑、索引状态、应用缓存是否适配——这些隐性断裂点,总在低峰期测试时完好,一到业务高峰就暴露。











