How do I merge two similar database-schema in PL/SQL?

前端 未结 3 1049
小鲜肉
小鲜肉 2021-01-15 02:44

The database-schema (Source and target) are very large (each has over 350 tables). I have got the task to somehow merge these two tables into one. The data itself (whats in

3条回答
  •  礼貌的吻别
    2021-01-15 03:15

    As promised to help in my comment, i had prepared a dynamic code which you can try to get the data merged with the source and target tables. The logic is as below:

    Step1: Get all the table names from the SOURCE schema. In the query below you can you need to replace the schema(owner) name respectively. For testing purpose i had taken only 1 table so when you run it,remove the table name filtering clause.

    Step2: Get the constrained columns names for the table. This is used to prepared the ON clause which would be later used for MERGE statement.

    Step3: Get the non-constrainted column names for the table. This would be used in UPDATE clause while using MERGE.

    Step4: Prepare the insert list when the data doesnot match ON conditon of MERGE statement.

    Read my inline comments to understand each step.

    CREATE OR REPLACE PROCEDURE COPY_TABLE
    AS
    Type OBJ_NME is table of varchar2(100) index by pls_integer;
    
    --To hold Table name
    v_obj_nm OBJ_NME ;
    
    --To hold Columns of table
    v_col_nm OBJ_NME;
    
    v_othr_col_nm OBJ_NME;
    on_clause VARCHAR2(2000);
    upd_clause VARCHAR2(4000);
    cntr number:=0;
    v_sql VARCHAR2(4000);
    
    col_list1  VARCHAR2(4000);
    col_list2  VARCHAR2(4000);
    col_list3  VARCHAR2(4000);
    col_list4  varchar2(4000);
    col_list5  VARCHAR2(4000);
    col_list6  VARCHAR2(4000);
    col_list7  VARCHAR2(4000);
    col_list8  varchar2(4000);
    
    BEGIN
    
    --Get Source table names
    SELECT OBJECT_NAME
    BULK COLLECT INTO v_obj_nm
    FROM all_objects 
    WHERE owner LIKE  'RU%' -- Replace `RU%` with your Source schema name here
    AND object_type = 'TABLE'
    and object_name ='TEST'; --remove this condition if you want this to run for all tables
    
    FOR I IN 1..v_obj_nm.count
    loop
    --Columns with Constraints 
      SELECT column_name
      bulk collect into v_col_nm 
      FROM user_cons_columns
      WHERE table_name = v_obj_nm(i);  
    
    --Columns without Constraints remain columns of table
    SELECT *
    BULK COLLECT INTO v_othr_col_nm
    from (
          SELECT column_name 
          FROM user_tab_cols
          WHERE table_name = v_obj_nm(i)
          MINUS
          SELECT column_name  
          FROM user_cons_columns
          WHERE table_name = v_obj_nm(i));
    
    --Prepare Update Clause  
     FOR l IN 1..v_othr_col_nm.count
      loop
       cntr:=cntr+1;
       upd_clause := 't1.'||v_othr_col_nm(l)||' = t2.' ||v_othr_col_nm(l);    
       upd_clause:=upd_clause ||' and ' ;
    
       col_list1:= 't1.'||v_othr_col_nm(l) ||',';
       col_list2:= col_list2||col_list1;   
    
       col_list5:= 't2.'||v_othr_col_nm(l) ||',';
       col_list6:= col_list6||col_list5;
    
       IF (cntr = v_othr_col_nm.count)
       THEN 
        --dbms_output.put_line('YES');
         upd_clause:=rtrim(upd_clause,' and');
         col_list2:=rtrim( col_list2,',');
         col_list6:=rtrim( col_list6,',');
       END IF;
         dbms_output.put_line(col_list2||col_list6); 
       --dbms_output.put_line(upd_clause);
       End loop;
      --Update caluse ends     
    
       cntr:=0; --Counter reset  
    
     --Prepare ON clause  
      FOR k IN 1..v_col_nm.count
      loop
       cntr:=cntr+1;
       --dbms_output.put_line(v_col_nm.count || cntr);
       on_clause := 't1.'||v_col_nm(k)||' = t2.' ||v_col_nm(k);    
       on_clause:=on_clause ||' and ' ;
    
       col_list3:= 't1.'||v_col_nm(k) ||',';
       col_list4:= col_list4||col_list3;    
    
       col_list7:= 't2.'||v_col_nm(k) ||',';
       col_list8:= col_list8||col_list7;    
    
       IF (cntr = v_col_nm.count)
       THEN 
        --dbms_output.put_line('YES');
        on_clause:=rtrim(on_clause,' and');
        col_list4:=rtrim( col_list4,',');
        col_list8:=rtrim( col_list8,',');
       end if;
    
       dbms_output.put_line(col_list4||col_list8);
     -- ON clause ends
    
     --Prepare merge Statement
    
        v_sql:= 'MERGE INTO '|| v_obj_nm(i)||' t1--put target schema name before v_obj_nm
                  USING (SELECT * FROM '|| v_obj_nm(i)||') t2-- put source schema name befire v_obj_nm here 
                  ON ('||on_clause||')
                  WHEN MATCHED THEN
                  UPDATE
                  SET '||upd_clause ||              
                  ' WHEN NOT MATCHED THEN
                  INSERT  
                  ('||col_list2||','
                    ||col_list4||
                  ')
                  VALUES
                  ('||col_list6||','
                    ||col_list8||          
                   ')';
    
          dbms_output.put_line(v_sql);   
          execute immediate v_sql;
      end loop;    
    End loop;
    END;
    /
    

    Execution:

    exec COPY_TABLE
    

    Output:

    anonymous block completed
    

    PS: i have tested this with a table with 2 columns out of which i was having unique key constraint .The DDL of table is as below:

    At the end i wish you could understand my code(you being a noob) and implement something similar if the above fails for your requirement.

     CREATE TABLE TEST
           (    COL2 NUMBER, 
                COLUMN1 VARCHAR2(20 BYTE), 
                CONSTRAINT TEST_UK1 UNIQUE (COLUMN1)  
           ) ;
    

提交回复
热议问题