ALTER TABLE ... MODIFY PARTITION READ ONLY 在 Oracle 12c+ 才支持,11g 及以下报 ORA-00905;需 ALTER ANY TABLE 权限、分区名大小写敏感、全局索引需显式更新,且只读仅拦截直接指定分区的 DML。
ALTER TABLE ... MODIFY PARTITION READ ONLY 为什么没生效
这条语句本身合法,但只在 oracle 12c 及以上版本支持;11g 或更早版本直接报错 ora-00905: missing keyword。不是语法写错了,是数据库版本卡住了。
常见错误现象:执行后查 DBA_TAB_PARTITIONS.READ_ONLY 仍是 NO,或后续仍能 INSERT 成功——大概率是因为没加 UPDATE GLOBAL INDEXes(如果表有全局索引),导致 DML 被绕过校验。
- 必须用具有
ALTER ANY TABLE权限的用户执行,普通SELECT权限不够 - 分区名区分大小写,且需确认拼写与
DBA_TAB_PARTITIONS.PARTITION_NAME完全一致(比如带双引号定义的分区) - 若该分区属于 Interval 分区表,且是系统自动生成的,某些旧补丁版本存在只读设置被忽略的 bug
READ ONLY 分区对 INSERT / UPDATE / DELETE 的实际拦截边界
只读属性只拦直接写入该分区的操作,不拦跨分区路由。比如向范围分区表插入一条数据,目标分区设为只读,但 INSERT 仍可能成功——只要数据实际落入其他可写分区。
真正被拦住的是明确指定分区的 DML,例如:INSERT INTO t PARTITION (p_2023) VALUES (...) 或 UPDATE t PARTITION (p_2022) SET ...,此时会立即报 ORA-14466: Data in a read-only partition or subpartition cannot be modified。
- 全局索引维护不受影响,但本地索引在只读分区上无法自动更新,DML 失败时索引状态可能异常
- 物化视图日志若涉及该分区,刷新可能失败,报
ORA-12008: error in materialized view refresh path - 导出工具如
expdp默认跳过只读分区,除非显式指定CONTENT=ALL
如何批量设置多个历史分区为只读
不能一次 MODIFY PARTITION 多个分区,Oracle 不支持逗号分隔列表。得用动态 SQL 循环生成语句。
安全做法是先查出待设只读的分区,排除当前活跃分区(比如按时间字段判断):
SELECT 'ALTER TABLE ' || table_name || ' MODIFY PARTITION ' || partition_name || ' READ ONLY;'
FROM dba_tab_partitions
WHERE table_owner = 'SCHEMA_NAME'
AND table_name = 'SALES'
AND partition_name LIKE 'P_2020%'
AND high_value < TO_DATE('2021-01-01', 'YYYY-MM-DD');
生成结果粘贴执行即可。注意别漏掉 ;,否则 SQL*Plus 会把下一行当续行处理。
- 生产环境建议加
AND read_only = 'NO'条件,避免重复执行报错 - 脚本里别硬编码
TO_DATE,用DATE'2021-01-01'更安全,避免 NLS 设置干扰 - 执行前检查是否有未提交事务锁住对应分区,否则会卡在 DDL 等待
只读分区 + 分区交换(EXCHANGE PARTITION)的兼容性坑
想把只读分区和临时表做 EXCHANGE PARTITION?可以,但前提是临时表也得是只读的,否则报 ORA-14097: column type or size mismatch in ALTER TABLE EXCHANGE PARTITION(实际是只读状态不匹配的隐式校验)。
更隐蔽的问题:交换后,原只读分区变成普通表,其只读属性丢失;而原普通表变成分区后,不会自动继承只读——必须手动再执行一次 MODIFY PARTITION READ ONLY。
- 交换操作本身不校验数据一致性,若临时表含非法数据,交换后查询只读分区也可能报错
- 如果用了
INCLUDING INDEXES,索引也会继承只读状态,但本地索引无法在只读分区上重建 - 分区交换后,
DBA_TAB_MODIFICATIONS中的历史统计信息不会自动清空,可能误导后续分析










