Oracle CTAS+rename 表重建方法操作总结

Oracle CTAS+rename 表重建方法操作总结

一、概述

使用 CREATE TABLE AS SELECT (CTAS) 配合 PARALLELNOLOGGING 进行,然后通过rename,实现表重建,是Oracle 中处理大表结构(如转分区表)最快速的方法之一!

但是这种方法不会自动迁移原表的非物理存储属性(例如约束、索引、触发器、依赖对象等等),都需要手工单独完成处理。

注意事项

复制代码
表和列的注释
表上列的默认值
表上列的约束
表上列的索引
表上相关触发器
表上的对象权限
引用该表的外键约束(需要重新指向)
表上的依赖对象(视图、存储过程、函数、包等),需要重新编译
表上的物化视图和物化视图日志
重建表和索引时,可能都会使用parallel+nologging模式来加快创建速度,完成后一定要将这些属性修改回来

备注:
关注表字段类型,那些LOB、long字段都是我们重建表是需要考虑的
一些同步机制,如果同步依赖rowid,由于重建表rowid会该表,可能造成实时同步失败,这些都是我们需要考虑的
工作完成后,检查一下所有依赖对象的有效性(建议在重建前保存快照,重建后与前面的快照比较)
如果依赖对象中存在一些私有对象(例如dblink等),我们用DBA用户重新编译是会出现编译错误,对于这种对象,必须以对应对象的所属者才能编译成功
重建的只是直接依赖对象,必须考虑那些间接依赖的对象(例如 view1依赖A表,view2依赖view1)
确保表空间有足够的空间(建议原表的1.5倍空闲空间)

二、具体CTAS+rename案例

0、准备测试表

复制代码
--创建表
drop table BIG_TABLE;
create table BIG_TABLE (
id number(10),
created_date date default sysdate,
lookup_id number(10),
data varchar2(50)
);
COMMENT ON TABLE big_table IS 'ctas tt';
--模拟数据
--准备测试数据
--插入 10000 条模拟数据(时间分布在 2023~2026 年)
INSERT INTO BIG_TABLE (id, created_date, lookup_id, data)
SELECT
  ROWNUM+2000,
  TO_DATE('2023-01-01', 'YYYY-MM-DD') + DBMS_RANDOM.VALUE(0, 1460),
  ROUND(DBMS_RANDOM.VALUE(1, 100)),
  DBMS_RANDOM.STRING('X', ROUND(DBMS_RANDOM.VALUE(5, 50)))
FROM DUAL
CONNECT BY LEVEL <= 10000;
commit;


--准备依赖对象(用于测试)
--授权
grant select,insert,update,delete on big_table to scott;

-- 创建索引
create index bita_created_date_i on BIG_TABLE(created_date);
create index bita_look_fk_i on BIG_TABLE(lookup_id);

-- 约束1:主键约束
ALTER TABLE BIG_TABLE
  ADD CONSTRAINT pk_big_table PRIMARY KEY (id);
  
-- 约束2:CHECK 约束,限制 lookup_id 范围
ALTER TABLE BIG_TABLE
  ADD CONSTRAINT chk_lookup_id CHECK (lookup_id BETWEEN 1 AND 100);

-- 约束3:NOT NULL 约束
ALTER TABLE BIG_TABLE
  MODIFY data CONSTRAINT nn_data NOT NULL;
 
-- 创建另一张表,外键约束引用该表
drop table FK_BIG_TABLE;
create table FK_BIG_TABLE (
id number,name varchar2(200),
FK_id number,
constraint fk_id_big foreign key (FK_id) references BIG_TABLE(id)
);
  
-- 触发器1:插入时自动生成 ID(序列 + 触发器)
drop sequence seq_big_table;
CREATE SEQUENCE seq_big_table START WITH 1001 INCREMENT BY 1;

CREATE OR REPLACE TRIGGER trg_big_table_bi
BEFORE INSERT ON BIG_TABLE
FOR EACH ROW
WHEN (NEW.id IS NULL)
BEGIN
  SELECT seq_big_table.NEXTVAL INTO :NEW.id FROM DUAL;
END;
/

-- 触发器2:更新时自动记录日志到日志表
drop table big_table_audit purge;
CREATE TABLE big_table_audit (
  operation    VARCHAR2(10),
  old_id       NUMBER(10),
  new_id       NUMBER(10),
  old_data     VARCHAR2(50),
  new_data     VARCHAR2(50),
  changed_by   VARCHAR2(30),
  changed_at   DATE DEFAULT SYSDATE
);

CREATE OR REPLACE TRIGGER trg_big_table_audit
AFTER UPDATE OR DELETE ON BIG_TABLE
FOR EACH ROW
BEGIN
  IF UPDATING THEN
    INSERT INTO big_table_audit (operation, old_id, new_id, old_data, new_data, changed_by)
    VALUES ('UPDATE', :OLD.id, :NEW.id, :OLD.data, :NEW.data, USER);
  ELSIF DELETING THEN
    INSERT INTO big_table_audit (operation, old_id, new_id, old_data, new_data, changed_by)
    VALUES ('DELETE', :OLD.id, NULL, :OLD.data, NULL, USER);
  END IF;
END;
/

-- 建立 3 个视图
-- 视图1:按年份汇总统计
CREATE OR REPLACE VIEW v_big_table_yearly AS
SELECT
  TO_CHAR(created_date, 'YYYY') AS year,
  COUNT(*)                       AS total_rows,
  MIN(created_date)              AS first_date,
  MAX(created_date)              AS last_date,
  AVG(lookup_id)                 AS avg_lookup_id
FROM BIG_TABLE
GROUP BY TO_CHAR(created_date, 'YYYY');

-- 视图2:按 lookup_id 分组统计
CREATE OR REPLACE VIEW v_big_table_lookup AS
SELECT
  lookup_id,
  COUNT(*)     AS cnt,
  MIN(data)    AS sample_data,
  MAX(created_date) AS latest_date
FROM BIG_TABLE
GROUP BY lookup_id;

-- 视图3:审计日志视图(关联原表)
CREATE OR REPLACE VIEW v_big_table_audit AS
SELECT
  a.operation,
  a.old_id,
  a.new_id,
  a.old_data,
  a.new_data,
  a.changed_by,
  a.changed_at
FROM big_table_audit a
ORDER BY a.changed_at DESC;

-- 创建存储过程
CREATE OR REPLACE PROCEDURE sp_insert_big_table(
  p_count        IN NUMBER,
  p_year_start   IN NUMBER DEFAULT 2023,
  p_year_end     IN NUMBER DEFAULT 2026
)
AS
  v_date_start DATE := TO_DATE(p_year_start || '-01-01', 'YYYY-MM-DD');
  v_date_end   DATE := TO_DATE(p_year_end   || '-12-31', 'YYYY-MM-DD');
  v_days       NUMBER := v_date_end - v_date_start;
BEGIN
  FOR i IN 1 .. p_count LOOP
    INSERT INTO BIG_TABLE (id, created_date, lookup_id, data)
    VALUES (
      NULL,
      v_date_start + DBMS_RANDOM.VALUE(0, v_days),
      ROUND(DBMS_RANDOM.VALUE(1, 100)),
      DBMS_RANDOM.STRING('X', ROUND(DBMS_RANDOM.VALUE(5, 50)))
    );
  END LOOP;
  COMMIT;
  DBMS_OUTPUT.PUT_LINE('成功插入 ' || p_count);
END sp_insert_big_table;
/

-- 函数:根据 ID 查询 data 字段
CREATE OR REPLACE FUNCTION fn_get_big_table_data(
  p_id IN BIG_TABLE.id%TYPE
)
RETURN BIG_TABLE.data%TYPE
AS
  v_data BIG_TABLE.data%TYPE;
BEGIN
  SELECT data INTO v_data
  FROM BIG_TABLE
  WHERE id = p_id;
  RETURN v_data;
EXCEPTION
  WHEN NO_DATA_FOUND THEN
    RETURN NULL;
END fn_get_big_table_data;
/

--创建package包
-- 包头
CREATE OR REPLACE PACKAGE pkg_big_table AS
  -- 按年份查询数据
  FUNCTION get_by_year(p_year IN NUMBER) RETURN SYS_REFCURSOR;
  -- 按 ID 删除数据
  PROCEDURE delete_by_id(p_id IN NUMBER);
  -- 统计总行数
  FUNCTION get_count RETURN NUMBER;
END pkg_big_table;
/

-- 包体
CREATE OR REPLACE PACKAGE BODY pkg_big_table AS

  FUNCTION get_by_year(p_year IN NUMBER) RETURN SYS_REFCURSOR
  AS
    v_cur SYS_REFCURSOR;
  BEGIN
    OPEN v_cur FOR
      SELECT id, created_date, lookup_id, data
      FROM BIG_TABLE
      WHERE TO_CHAR(created_date, 'YYYY') = TO_CHAR(p_year)
      ORDER BY created_date;
    RETURN v_cur;
  END get_by_year;

  PROCEDURE delete_by_id(p_id IN NUMBER)
  AS
  BEGIN
    DELETE FROM BIG_TABLE WHERE id = p_id;
    IF SQL%ROWCOUNT = 0 THEN
      RAISE_APPLICATION_ERROR(-20001, '未找到 ID = ' || p_id);
    END IF;
    COMMIT;
  END delete_by_id;

  FUNCTION get_count RETURN NUMBER
  AS
    v_cnt NUMBER;
  BEGIN
    SELECT COUNT(*) INTO v_cnt FROM BIG_TABLE;
    RETURN v_cnt;
  END get_count;

END pkg_big_table;
/


set linesize 200 pagesize 999
col constraint_name format a20
col constraint_type format a15
col triggering_event format a20
col trigger_name format a20
col view_name format a20
col object_name format a20
col object_type format a20
-- 验证数据量
SELECT COUNT(*) FROM BIG_TABLE;
-- 验证约束
SELECT constraint_name, constraint_type, status
FROM user_constraints
WHERE table_name = 'BIG_TABLE';

-- 验证触发器
SELECT trigger_name, triggering_event, status
FROM user_triggers
WHERE table_name IN ('BIG_TABLE', 'BIG_TABLE_AUDIT');

-- 验证视图
SELECT view_name FROM user_views WHERE view_name LIKE 'V_BIG_TABLE%';
SELECT object_name, object_type, status FROM user_objects WHERE object_name LIKE 'V_BIG_TABLE%';

-- 验证存储过程
SELECT object_name, object_type, status
FROM user_objects
WHERE object_name IN ('SP_INSERT_BIG_TABLE', 'FN_GET_BIG_TABLE_DATA','PKG_BIG_TABLE');

-- 验证包
set serveroutput on 
SELECT pkg_big_table.get_count FROM DUAL;
 GET_COUNT
----------
     10000

1、获取表定义和表大小

复制代码
set longc 9999
set long 99999
set linesize 400 pagesize 400
select dbms_metadata.get_ddl('TABLE',upper('BIG_TABLE'),upper('user1')) from dual;
或者
select dbms_metadata.get_ddl('TABLE',upper('&i_table_name'),upper('&i_owner')) from dual;

结果
------------------------------------------------------------------------------------------------------------
  CREATE TABLE "USER1"."BIG_TABLE"
   (    "ID" NUMBER(10,0),
        "CREATED_DATE" DATE DEFAULT sysdate,
        "LOOKUP_ID" NUMBER(10,0),
        "DATA" VARCHAR2(50) CONSTRAINT "NN_DATA" NOT NULL ENABLE,
         CONSTRAINT "PK_BIG_TABLE" PRIMARY KEY ("ID")
  USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
  BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
  TABLESPACE "APP_DATA"  ENABLE,
         CONSTRAINT "CHK_LOOKUP_ID" CHECK (lookup_id BETWEEN 1 AND 100) ENABLE
   ) SEGMENT CREATION IMMEDIATE
  PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255
 NOCOMPRESS LOGGING
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
  BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
  TABLESPACE "APP_DATA"
  
-- 表大小查询(包括索引)
with temp_seg as (
select owner,segment_name
from dba_lobs
where TABLE_NAME = upper('BIG_TABLE') AND OWNER = upper('USER1')
union
select owner,segment_name
from dba_segments
where segment_name = upper('BIG_TABLE') AND OWNER = upper('USER1')
union
select owner,index_name
from dba_indexes
where TABLE_NAME = upper('BIG_TABLE') AND OWNER = upper('USER1')
)
select a.tablespace_name,sum(a.bytes)/1024/1024
from dba_segments a,temp_seg b
where a.owner =b.owner and a.segment_name=b.segment_name
group by a.tablespace_name;

2、获取表的依赖对象定义(最关键部分)

(先将获取的结果保存,后续会需要使用这些定义来重新创建)

复制代码
1)获取表注释
SELECT 'COMMENT ON TABLE '||table_Name||' IS ''' || COMMENTS || ''';' 
FROM DBA_TAB_COMMENTS 
WHERE  TABLE_NAME = upper('BIG_TABLE') AND OWNER = upper('USER1');

结果:
COMMENT ON TABLE BIG_TABLE IS 'ctastt';

-- 获取列注释
SELECT 'COMMENT ON COLUMN '||table_Name||'.'|| COLUMN_NAME || ' IS ''' || COMMENTS || ''';' 
FROM DBA_COL_COMMENTS 
WHERE  TABLE_NAME = upper('BIG_TABLE') AND OWNER = upper('USER1') AND COMMENTS IS NOT NULL;

结果:
无

2)获取源表索引结构
-- 提取原表上所有索引的创建语句
SELECT DBMS_METADATA.GET_DDL('INDEX', INDEX_NAME, upper('USER1')) 
FROM DBA_INDEXES 
WHERE TABLE_NAME = upper('BIG_TABLE') AND OWNER = upper('USER1');

结果:
CREATE INDEX "USER1"."BITA_CREATED_DATE_I" ON "USER1"."BIG_TABLE" ("CREATED_DATE")
PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
TABLESPACE "APP_DATA"

CREATE INDEX "USER1"."BITA_LOOK_FK_I" ON "USER1"."BIG_TABLE" ("LOOKUP_ID")
PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
TABLESPACE "APP_DATA"

CREATE UNIQUE INDEX "USER1"."PK_BIG_TABLE" ON "USER1"."BIG_TABLE" ("ID")
PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
TABLESPACE "APP_DATA"


3)提取原表上所有除NOT NULL外的约束(主键、外键、检查等)
SELECT DBMS_METADATA.GET_DDL('CONSTRAINT', CONSTRAINT_NAME, upper('USER1')) 
FROM DBA_CONSTRAINTS 
WHERE TABLE_NAME = upper('BIG_TABLE') AND OWNER = upper('USER1');

结果:
ALTER TABLE "USER1"."BIG_TABLE" MODIFY ("DATA" CONSTRAINT "NN_DATA" NOT NULL ENABLE)

ALTER TABLE "USER1"."BIG_TABLE" ADD CONSTRAINT "CHK_LOOKUP_ID" CHECK (lookup_id BETWEEN 1 AND 100) ENABLE

ALTER TABLE "USER1"."BIG_TABLE" ADD CONSTRAINT "PK_BIG_TABLE" PRIMARY KEY ("ID")
USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
TABLESPACE "APP_DATA"  ENABLE



4)获取表的授权
select 'grant '||privilege||' on '||owner||'.'||table_name||' to '||grantee||';'  
from dba_tab_privs
where TABLE_NAME = upper('BIG_TABLE') AND OWNER = upper('USER1');

结果:
grant DELETE on USER1.BIG_TABLE to SCOTT;
grant INSERT on USER1.BIG_TABLE to SCOTT;
grant SELECT on USER1.BIG_TABLE to SCOTT;
grant UPDATE on USER1.BIG_TABLE to SCOTT;


5)提取触发器源码
SELECT DBMS_METADATA.GET_DDL('TRIGGER', TRIGGER_NAME, upper('USER1')) 
FROM DBA_TRIGGERS 
WHERE TABLE_NAME = upper('BIG_TABLE') AND OWNER = upper('USER1');

结果:
CREATE OR REPLACE TRIGGER "USER1"."TRG_BIG_TABLE_BI"
BEFORE INSERT ON BIG_TABLE
FOR EACH ROW
 WHEN (NEW.id IS NULL) BEGIN
  SELECT seq_big_table.NEXTVAL INTO :NEW.id FROM DUAL;
END;
ALTER TRIGGER "USER1"."TRG_BIG_TABLE_BI" ENABLE

CREATE OR REPLACE TRIGGER "USER1"."TRG_BIG_TABLE_AUDIT"
AFTER UPDATE OR DELETE ON BIG_TABLE
FOR EACH ROW
BEGIN
  IF UPDATING THEN
    INSERT INTO big_table_audit (operation, old_id, new_id, old_data, new_data, changed_by)
    VALUES ('UPDATE', :OLD.id, :NEW.id, :OLD.data, :NEW.data, USER);
  ELSIF DELETING THEN
    INSERT INTO big_table_audit (operation, old_id, new_id, old_data, new_data, changed_by)
    VALUES ('DELETE', :OLD.id, NULL, :OLD.data, NULL, USER);
  END IF;
END;
ALTER TRIGGER "USER1"."TRG_BIG_TABLE_AUDIT" ENABLE


6)重建触发器,需要先删除,删除触发器语句
SELECT 'DROP TRIGGER '||OWNER||'.'||TRIGGER_NAME 
FROM DBA_TRIGGERS 
WHERE TABLE_NAME = upper('BIG_TABLE') AND OWNER = upper('USER1');

结果:
DROP TRIGGER "USER1"."TRG_BIG_TABLE_BI";
DROP TRIGGER "USER1"."TRG_BIG_TABLE_AUDIT";


7)依赖对象重建:一般可以使用如下方式完成
select 'alter '||decode(type,'PACKAGE BODY','PACKAGE',type)||' '||owner||'.'||name||' compile;'
  from dba_dependencies a
 where a.referenced_name = upper('BIG_TABLE')
   and a.referenced_owner = upper('USER1');  

结果:
alter TRIGGER USER1.TRG_BIG_TABLE_AUDIT compile;
alter FUNCTION USER1.FN_GET_BIG_TABLE_DATA compile;
alter PACKAGE USER1.PKG_BIG_TABLE compile;
alter VIEW USER1.V_BIG_TABLE_YEARLY compile;
alter TRIGGER USER1.TRG_BIG_TABLE_BI compile;
alter VIEW USER1.V_BIG_TABLE_LOOKUP compile;
alter PROCEDURE USER1.SP_INSERT_BIG_TABLE compile;

8)获取依赖对象的状态,记录下来,用于前后比较
select a.owner,a.name,a.type,b.status  
from dba_DEPENDENCIES a,dba_objects b 
where a.REFERENCED_OWNER=upper('USER1') and a.REFERENCED_NAME=upper('BIG_TABLE') and a.NAME=b.object_name;

结果
OWNER      NAME                           TYPE               STATUS
---------- ------------------------------ ------------------ -------
USER1      V_BIG_TABLE_LOOKUP             VIEW               VALID
USER1      SP_INSERT_BIG_TABLE            PROCEDURE          VALID
USER1      PKG_BIG_TABLE                  PACKAGE BODY       VALID
USER1      PKG_BIG_TABLE                  PACKAGE BODY       VALID
USER1      V_BIG_TABLE_YEARLY             VIEW               VALID
USER1      TRG_BIG_TABLE_BI               TRIGGER            VALID
USER1      TRG_BIG_TABLE_AUDIT            TRIGGER            VALID
USER1      FN_GET_BIG_TABLE_DATA          FUNCTION           VALID

9)查询外键约束,引用该表的
set linesize 200 pagesize 999
col table_name format a15
col owner format a10
col constraint_name  format a20
col r_owner  format a10
col constraint_type  format a20
col r_constraint_name  format a20  
col table_name  format a20  
select a.table_name,
       a.owner,
       a.constraint_name,
       a.constraint_type,
       a.r_owner,
       a.r_constraint_name,
       b.table_name
  from dba_constraints a, dba_constraints b
 where a.constraint_type = 'R'
   and a.r_constraint_name = b.constraint_name
   and a.r_owner = b.owner
   and b.table_name = upper('BIG_TABLE')
   and b.owner=upper('USER1');

a.table_name  /* 外键引用待重命名的表 *
a.r_constraint_name, /*被外键引用的约束名*/
b.table_name   /*被外键引用的表名*/  

结果:
TABLE_NAME           OWNER      CONSTRAINT_NAME      CONSTRAINT_TYPE      R_OWNER    R_CONSTRAINT_NAME    TABLE_NAME
-------------------- ---------- -------------------- ----------------   ---------- -------------------- 
FK_BIG_TABLE         USER1      FK_ID_BIG;            R                    USER1      PK_BIG_TABLE         BIG_TABLE

删除外键约束和重建外键约束语句:
alter table FK_BIG_TABLE drop constraint FK_ID_BIG;
alter table FK_BIG_TABLE add constraint fk_id_big foreign key (FK_id) references BIG_TABLE_NEW(id);


10)物化视图日志:物化视图日志是为了快速刷新准备的,而且从dba_dependencies 这张依赖表中无法查找出来的,但是,对于这个对象,我们一定要保持谨慎和敬畏,因为如果表上存在物化视图日志对象的话,那么这张表无法完成rename(在一个变更的晚上,其它什么都OK了,突然遇到一个这样的问题,还得找开发确认,是非常被动的,整个变更很有可能因为这个无法确认而取消),会直接报错,查找表上的物化视图日志对象方法如下:
select master,log_table
 from user_mview_logs a
where master in ('BIG_TABLE');  

3、正式开始CTAS

1)设置表为只读模式,禁止增删改,确保数据无变化(业务会受到影响)

复制代码
alter table user1.big_table read only;

2)开始CTAS

复制代码
drop table big_table_new;
create table big_table_new (
id ,
created_date default sysdate,
lookup_id ,
data 
)
nologging parallel 4
as
select /*+ parallel(a 4) */ * from user1.big_table a;

备注:
可以通过指定tablespace,将表存放至新的表空间
建议通过上面的方式,将默认值加入建表语句,以防止遗漏掉
表的结构,通过前面获取表定义得到

4、CTAS完成后,依赖对象处理(重要部分)

复制代码
1)可选项、修改原表的索引名称,方法如下
重建后索引的名字是否必须和以前的一样,如果需要一样,则必须将当前使用的索引名字先rename,否则创建的时候会出现索引名字已经存在的错误
select 'alter index ' || owner || '.' || index_name || ' rename to ' ||substr(index_name, 1, 26) || '_old;'
  from dba_indexes a
 where a.table_owner = upper('USER1')
   AND A.table_name = upper('BIG_TABLE');
   
alter index USER1.BITA_CREATED_DATE_I rename to BITA_CREATED_DATE_I_old;
alter index USER1.BITA_LOOK_FK_I rename to BITA_LOOK_FK_I_old;
alter index USER1.PK_BIG_TABLE rename to PK_BIG_TABLE_old;


2)注释、约束、索引的提前创建
COMMENT ON TABLE BIG_TABLE_NEW IS 'ctastt';
CREATE INDEX "USER1"."BITA_CREATED_DATE_I" ON "USER1"."BIG_TABLE_NEW" ("CREATED_DATE") NOLOGGING parallel 4;
CREATE INDEX "USER1"."BITA_LOOK_FK_I" ON "USER1"."BIG_TABLE_NEW" ("LOOKUP_ID") NOLOGGING parallel 4;
CREATE UNIQUE INDEX "USER1"."PK_BIG_TABLE_NEW" ON "USER1"."BIG_TABLE_NEW" ("ID") NOLOGGING parallel 4;
ALTER TABLE "USER1"."BIG_TABLE_NEW" MODIFY ("DATA" CONSTRAINT "NN_DATA" NOT NULL ENABLE);
ALTER TABLE "USER1"."BIG_TABLE_NEW" ADD CONSTRAINT "CHK_LOOKUP_ID1" CHECK (lookup_id BETWEEN 1 AND 100) ENABLE;
ALTER TABLE "USER1"."BIG_TABLE_NEW" ADD CONSTRAINT "PK_BIG_TABLE_NEW" PRIMARY KEY ("ID") USING INDEX ENABLE ; 
特别注意:约束名称,需要修改(否则会冲突)。索引名称也一样。

3)引用原表的外键约束,处理如下
alter table FK_BIG_TABLE drop constraint FK_ID_BIG;
alter table FK_BIG_TABLE add constraint fk_id_big foreign key (FK_id) references BIG_TABLE_NEW(id);

4)修改属性(改为logging noparallel属性)
alter table big_table_new logging noparallel;
alter index USER1.BITA_CREATED_DATE_I logging noparallel;
alter index USER1.BITA_LOOK_FK_I logging noparallel;
alter index USER1.PK_BIG_TABLE_NEW logging noparallel;

备注:索引批量修改的语句查询
select 'alter index ' || owner || '.' || index_name || ' logging noparallel;'
  from dba_indexes a
 where a.table_owner = upper('USER1')
   AND A.table_name = upper('BIG_TABLE');

5、rename重命名表

复制代码
ALTER TABLE BIG_TABLE RENAME TO BIG_TABLE_OLD;
ALTER TABLE BIG_TABLE_NEW RENAME TO BIG_TABLE;

6、依赖对象继续处理(重要部分)

复制代码
1)权限重新授予
grant DELETE on USER1.BIG_TABLE to SCOTT;
grant INSERT on USER1.BIG_TABLE to SCOTT;
grant SELECT on USER1.BIG_TABLE to SCOTT;
grant UPDATE on USER1.BIG_TABLE to SCOTT;

2)触发器重建
--先删除
DROP TRIGGER "USER1"."TRG_BIG_TABLE_BI";
DROP TRIGGER "USER1"."TRG_BIG_TABLE_AUDIT";
--再重建
CREATE OR REPLACE TRIGGER "USER1"."TRG_BIG_TABLE_BI"
BEFORE INSERT ON BIG_TABLE
FOR EACH ROW
 WHEN (NEW.id IS NULL) BEGIN
  SELECT seq_big_table.NEXTVAL INTO :NEW.id FROM DUAL;
END;
/

CREATE OR REPLACE TRIGGER "USER1"."TRG_BIG_TABLE_AUDIT"
AFTER UPDATE OR DELETE ON BIG_TABLE
FOR EACH ROW
BEGIN
  IF UPDATING THEN
    INSERT INTO big_table_audit (operation, old_id, new_id, old_data, new_data, changed_by)
    VALUES ('UPDATE', :OLD.id, :NEW.id, :OLD.data, :NEW.data, USER);
  ELSIF DELETING THEN
    INSERT INTO big_table_audit (operation, old_id, new_id, old_data, new_data, changed_by)
    VALUES ('DELETE', :OLD.id, NULL, :OLD.data, NULL, USER);
  END IF;
END;
/

3)依赖对象重新编译
alter TRIGGER USER1.TRG_BIG_TABLE_AUDIT compile;
alter FUNCTION USER1.FN_GET_BIG_TABLE_DATA compile;
alter PACKAGE USER1.PKG_BIG_TABLE compile;
alter VIEW USER1.V_BIG_TABLE_YEARLY compile;
alter TRIGGER USER1.TRG_BIG_TABLE_BI compile;
alter VIEW USER1.V_BIG_TABLE_LOOKUP compile;
alter PROCEDURE USER1.SP_INSERT_BIG_TABLE compile;

7、验证

复制代码
set linesize 200 pagesize 999
col constraint_name format a20
col constraint_type format a15
col triggering_event format a20
col trigger_name format a20
col view_name format a20
col object_name format a20
col object_type format a20
-- 验证数据量
SELECT COUNT(*) FROM BIG_TABLE;
-- 验证约束
SELECT constraint_name, constraint_type, status
FROM user_constraints
WHERE table_name = 'BIG_TABLE';

-- 验证触发器
SELECT trigger_name, triggering_event, status
FROM user_triggers
WHERE table_name IN ('BIG_TABLE', 'BIG_TABLE_AUDIT');

-- 验证视图
SELECT view_name FROM user_views WHERE view_name LIKE 'V_BIG_TABLE%';
SELECT object_name, object_type, status FROM user_objects WHERE object_name LIKE 'V_BIG_TABLE%';

-- 验证存储过程
SELECT object_name, object_type, status
FROM user_objects
WHERE object_name IN ('SP_INSERT_BIG_TABLE', 'FN_GET_BIG_TABLE_DATA','PKG_BIG_TABLE');

--依赖对象的前后比较
select a.owner,a.name,a.type,b.status  
from dba_DEPENDENCIES a,dba_objects b 
where a.REFERENCED_OWNER=upper('USER1') and a.REFERENCED_NAME=upper('BIG_TABLE') and a.NAME=b.object_name;

结果应该如下
OWNER      NAME                           TYPE               STATUS
---------- ------------------------------ ------------------ -------
USER1      V_BIG_TABLE_LOOKUP             VIEW               VALID
USER1      SP_INSERT_BIG_TABLE            PROCEDURE          VALID
USER1      PKG_BIG_TABLE                  PACKAGE BODY       VALID
USER1      PKG_BIG_TABLE                  PACKAGE BODY       VALID
USER1      V_BIG_TABLE_YEARLY             VIEW               VALID
USER1      TRG_BIG_TABLE_BI               TRIGGER            VALID
USER1      TRG_BIG_TABLE_AUDIT            TRIGGER            VALID
USER1      FN_GET_BIG_TABLE_DATA          FUNCTION           VALID
相关推荐
Dovis(誓平步青云)15 分钟前
同一条路线在手机和平板上怎样换一种排版
android·开发语言·数据库·智能手机·音视频
czhc114007566320 分钟前
EF Core 数据访问三件套:池化 · 仓储 · 不跟踪
数据库
whcyhhh21 分钟前
头歌实践教学平台:大数据存储2023(十三1)
大数据·数据库·python
TDengine (老段)26 分钟前
TDengine 应用案例 — IT 运维与可观测性
大数据·运维·数据库·制造·时序数据库·tdengine·涛思数据
疯狂打码的少年27 分钟前
【数据库技术】关系代数连接运算(等值连接/自然连接/外连接)
jvm·数据库·笔记·oracle
小王C语言33 分钟前
MySQL 视图:视图是什么,视图和基表之间的关系
数据库·mysql
程序员-Benothing35 分钟前
什么是 MySQL 的主从同步机制?它是如何实现的?
android·数据库·mysql
Wang's Blog38 分钟前
Vibe Coding一人即团队系列31:基于Claude Code实现登录注册与数据库联动
数据库·人工智能
2601_9620566042 分钟前
Spring Boot——日志介绍和配置
java·数据库·spring boot