shrink space 无法被 PL/SQL 循环自动执行,因其是依赖物理状态的 DDL 操作,受行移动禁用、MSSM 表空间、特殊对象类型、锁冲突等硬性限制;真正可脚本化的是准备、检查与分批调度。
不能靠 pl/sql 脚本“自动化”清理碎片——shrink space 必须手动触发,且需严格满足前提条件,否则直接报错或无效。
为什么 shrink space 无法被 PL/SQL 循环自动执行
Oracle 的 shrink space 是 DDL 操作,依赖表的物理状态和段管理方式。PL/SQL 块中执行它时,会遇到以下硬性限制:
-
ORA-10636:表未启用行移动(enable row movement)——必须提前对每张表单独执行,不能靠脚本批量隐式完成 -
ORA-10637:表位于手动段空间管理(MSSM)表空间——shrink只支持自动段空间管理(ASSM),脚本无法自动迁移表空间 - 物化视图基表、IOT 表、含函数索引或 SecureFile LOB 的表——
shrink直接拒绝执行,且 PL/SQL 无法在运行时动态跳过这些对象而不报错 - 执行期间若发生锁冲突(如其他会话正在 DML),语句会阻塞或失败,而标准 PL/SQL
EXECUTE IMMEDIATE不带重试/超时控制,容易中断整个流程
shrink space cascade 和 shrink space compact 的实际差异
这两个参数不是可选开关,而是决定是否释放空间、是否影响业务的关键行为:
-
shrink space compact:只重组数据块、整理空闲空间,但不移动高水位线(HWM)——不释放磁盘空间,业务低峰期可安全执行 -
shrink space(无参数):下调 HWM 并释放空间——需要独占访问,会短暂锁表,必须配合enable row movement -
shrink space cascade:等价于先对表shrink space,再对其所有索引逐个执行shrink space——耗时更长,且索引 shrink 同样要求其所在表空间为 ASSM
常见误用:shrink space cascade 并不会自动处理外键约束或依赖视图,也不会重建失效的索引——那些仍需人工干预。
真正能脚本化的只有“准备+检查+分批调度”环节
你可以在 PL/SQL 中安全完成的,是碎片识别、条件过滤和分批次发起 shrink,但每一步都得加防护:
- 先查出可 shrink 的表:
SELECT table_name FROM user_tables WHERE segment_space_management = 'AUTO' AND iot_type IS NULL AND temporary = 'N' - 对每张表检查是否已启用行移动:
SELECT row_movement FROM user_tables WHERE table_name = 'T1',未启用则先ALTER TABLE t1 ENABLE ROW MOVEMENT - 用
BULK COLLECT批量取表名,循环中加EXCEPTION WHEN OTHERS THEN dbms_output.put_line('skip ' || tname || ': ' || sqlerrm)避免单表失败中断全部 - 绝不写成
EXECUTE IMMEDIATE 'ALTER TABLE ' || tname || ' SHRINK SPACE CASCADE'一揽子执行——大表 shrink 可能持续数分钟,应拆成 compact + 空闲时段再 shrink
最容易被忽略的收尾动作
shrink 完成后,表的统计信息不会自动更新,num_rows 和 blocks 仍反映旧值,导致后续执行计划错误。必须显式补一句:
EXEC DBMS_STATS.GATHER_TABLE_STATS(ownname => USER, tabname => 'T1', cascade => TRUE);
而且,如果该表被视图、包、触发器引用,shrink 可能使这些对象变为 INVALID,需额外跑 @?/rdbms/admin/utlrp.sql 或查询 dba_objects where status = 'INVALID' 后手工编译——这部分没法藏进一个 shrink 脚本里一并解决。


















