Oracle中ALTER TABLE MOVE不能批量执行,必须手动生成SQL脚本或用PL/SQL动态执行;MOVE后索引全部失效需重建;分区表须按分区移动;操作全程锁表且不更新统计信息,需手动收集。
ALTER TABLE MOVE 不能批量执行,必须生成语句或用动态SQL
oracle 没有 alter user ... move all tables 这类语法。alter user default tablespace 只影响后续新建对象,对已有表完全无效——这是最常踩的坑。必须逐个表触发 alter table ... move tablespace,方式只有两种:手动生成 sql 脚本,或用 pl/sql 动态执行。
用 SELECT 拼接生成 ALTER TABLE MOVE 语句(推荐初试)
在 SQL*Plus 或 SQL Developer 中运行以下查询,把结果复制出来执行:
SELECT 'ALTER TABLE ' || table_name || ' MOVE TABLESPACE NEW_TBS;' FROM user_tables WHERE tablespace_name != 'NEW_TBS' AND status = 'VALID';
注意几个关键点:
- 表名含大小写或特殊字符时,必须加双引号,比如
"MyTable";可用SELECT '"' || table_name || '"' FROM ...生成带引号的版本 - 如果表有 LOB 字段,上面语句只移动表段,LOB 段仍留在原表空间;需额外拼接
ALTER TABLE ... MOVE LOB(...) STORE AS (TABLESPACE NEW_TBS) - 语句末尾必须有分号,否则粘贴到 SQL 窗口会报错
- 不要漏掉
AND status = 'VALID',避免对无效表(如被 DROP 后未清理的残留)操作
MOVE 后索引全部失效,必须重建
ALTER TABLE ... MOVE 会把所有普通索引置为 UNUSABLE 状态,查 user_indexes 可确认:SELECT index_name, status FROM user_indexes。常见错误是忽略这步,之后执行带索引条件的查询直接报 ORA-01502。
重建索引建议用:
SELECT 'ALTER INDEX ' || index_name || ' REBUILD TABLESPACE NEW_TBS;' FROM user_indexes WHERE status = 'UNUSABLE';
或者更稳妥地按表重建(避免遗漏):
SELECT 'ALTER INDEX ' || i.index_name || ' REBUILD TABLESPACE NEW_TBS;' FROM user_indexes i JOIN user_tables t ON i.table_name = t.table_name WHERE t.tablespace_name = 'NEW_TBS' AND i.status = 'UNUSABLE';
分区表、LOB、锁与事务风险不能跳过
分区表不能直接 ALTER TABLE ... MOVE,会报 ORA-14511;必须按分区操作:
SELECT 'ALTER TABLE ' || table_name || ' MOVE PARTITION ' || partition_name || ' TABLESPACE NEW_TBS;' FROM user_tab_partitions WHERE tablespace_name != 'NEW_TBS';
其他容易被忽略的点:
-
MOVE是 DDL,全程锁表,大表可能阻塞业务;建议在低峰期执行,或用ONLINE(12c+ 对部分场景支持,但不适用于所有表) - MOVE 不保留原物理顺序,HWM 重置,但统计信息不会自动更新,迁移后应手动
DBMS_STATS.GATHER_TABLE_STATS - 若目标表空间是 ASSM,但没显式指定
SEGMENT SPACE MANAGEMENT AUTO,新段仍可能沿用旧存储参数——建议在MOVE语句中加上STORAGE子句控制初始分配











