Oracle 如何在PL/SQL中合并两个类似的数据库模式?

Oracle 如何在PL/SQL中合并两个类似的数据库模式?,oracle,primary-key,database-schema,Oracle,Primary Key,Database Schema,数据库模式(源和目标)非常大(每个都有350多个表)。我的任务是以某种方式将这两个表合并为一个表。必须迁移数据本身(表中的内容)。我必须小心,在合并模式之前或合并模式时,主键没有双重条目。是否有人已经这样做过,并且能够为我提供他的解决方案,或者有人能够帮助我找到完成任务的方法?我的方法都失败了,我的导师只是告诉我在线获取帮助:/ 按照我的方法: 我已经尝试使用“all_constraints”表从我的数据库中获取所有PK SELECT cols.table_name, cols.column_n

数据库模式(源和目标)非常大(每个都有350多个表)。我的任务是以某种方式将这两个表合并为一个表。必须迁移数据本身(表中的内容)。我必须小心,在合并模式之前或合并模式时,主键没有双重条目。是否有人已经这样做过,并且能够为我提供他的解决方案,或者有人能够帮助我找到完成任务的方法?我的方法都失败了,我的导师只是告诉我在线获取帮助:/

按照我的方法: 我已经尝试使用“all_constraints”表从我的数据库中获取所有PK

SELECT cols.table_name, cols.column_name, cols.position, cons.status, cons.owner
FROM all_constraints cons, all_cons_columns cols
WHERE cols.owner = 'DB'
AND cons.constraint_type = 'P'  
AND cons.constraint_name = cols.constraint_name
AND cons.owner = cols.owner
ORDER BY cols.table_name, cols.position;
我还“知道”主键必须有一个序列才能向其添加值:

CREATE SEQUENCE seq_pk_addition
MINVALUE 1
MAXVALUE 99999999999999999999
START WITH 1
INCREMENT BY 1
CACHE 20;
因为如果涉及到pl/sql(或一般的sql),我就是一个noob 那么我下一步该怎么做/

以下是数据库ERD的链接:

病毒扫描:


哦,天哪通常情况下,这样的问题会很快以“过于宽泛”而结束,但我们需要支持邪恶顾问的受害者

至于工作,我需要一个有经验的专家一周的全职时间,加上一个有经验的QA工程师两天的质量检查

首先,这种复杂的数据合并不可能在第一次尝试时就奏效。这意味着您将需要两个模式的测试副本,这些副本可以轻松地重建。你需要一个地方来尝试一下。通常,这是通过导出模式和空的dev数据库来完成的

接下来,您需要两个模式足够接近,以便能够比较数据。我会导入上面提到的导出文件。如果架构名称与导入期间重命名的架构名称相同

接下来,我将再次检查结构是否完全相同,查询如下

 SELECT a.owner, a.table_name, b.owner, b.table_name
   FROM all_tables a 
   FULL JOIN all_tables b 
     ON a.table_name = b.table_name
    AND a.owner = 'SCHEMAA' 
    AND b.owner = 'SCHEMAB'
  WHERE a.owner IS NULL or b.owner IS NULL;
接下来,我将检查主键和唯一键是否重叠:

 SELECT id FROM schemaa.table1
 INTERSECT
 SELECT id FROM schemab.table1;
由于有300多个表,我将生成这些查询:

 DECLARE 
   stmt VARCHAR2(30000);
   n NUMBER;
   schema_a CONSTANT VARCHAR2(128 BYTE) := 'SCHEMAA';
   schema_b CONSTANT VARCHAR2(128 BYTE) := 'SCHEMAB';
 BEGIN
   FOR c IN (SELECT owner, constraint_name, table_name,
                    (SELECT LISTAGG(column_name,',') WITHIN GROUP (ORDER BY position)
                       FROM all_cons_columns c
                      WHERE s.owner = c.owner
                        AND s.constraint_name = c.constraint_name) AS cols
               FROM all_constraints s
              WHERE s.constraint_type IN ('P') 
                AND s.owner = schema_a) 
   LOOP
     dbms_output.put_line('Checking pk '||c.constraint_name||' on table '||c.table_name);
     stmt := 'SELECT count(*) FROM '||schema_a||'.'||c.table_name
          ||' JOIN '||schema_b||'.'||c.table_name
          || ' USING ('||c.cols||')';
     --dbms_output.put_line('Query '||stmt);
     EXECUTE IMMEDIATE stmt INTO n;
     dbms_output.put_line('Found '||n||' overlapping primary keys in table '||c.table_name);
   END LOOP;
 END;
 /

首先,对于350个表,很可能需要一个
动态SQL

  • 用所有表名声明一个
    游标
    或一个
    集合-VARCHAR2表
  • 声明一个字符串变量以生成
    动态SQL
  • 通过表名的整个列表循环
    ,并为每个表生成一个字符串,该字符串将通过
    EXECUTE IMMEDIATE
    命令作为SQL执行
  • 将要构建的动态SQL应该将源表中的值插入到目标表中。如果PK已经存在,则应在目标表中检查表示上次更新日期的字段,如果在源表中PK大于目标表中PK,则在目标表中执行更新,否则不执行任何操作

  • 正如我在评论中承诺的那样,我已经准备了一个动态代码,您可以尝试将数据
    与源表和目标表合并。逻辑如下:

    步骤1:从
    模式中获取所有表名。在下面的查询中,您可能需要分别替换模式(所有者)名称。出于测试目的,我只使用了1个表,所以在运行它时,请删除表名筛选子句

    步骤2:获取表的受约束列名称。这用于准备
    ON
    子句,该子句稍后将用于
    MERGE
    语句

    步骤3:获取表的非约束列名。这将在使用
    MERGE
    时在
    UPDATE
    子句中使用

    步骤4:在
    MERGE
    语句的
    条件下,当数据与
    不匹配时,准备
    insert
    列表

    阅读我的内联评论,了解每一步

    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;
    /
    
    执行:

    exec COPY_TABLE
    
    输出:

    anonymous block completed
    
    PS:我已经用一个有两列的表测试了这一点,其中我有唯一的键约束。表的DDL如下所示:

    最后,我希望你能理解我的代码(你是一个noob),并实现类似的东西,如果上面的要求失败

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

    如果涉及到pl/sql,我就是一个noob
    不相关,但如果你是
    noob
    你必须学会对你的专业知识赋予你的任务说
    。顺便说一句,有很多复制工具,试试谷歌吧。或者你可以构建动态的
    MERGE
    语句来实现这一点,如果我说不,我不想做这项任务,我必须忍受后果(因为我得不到学位),因为我的顾问不在乎你能不能做,他们希望你做!这就是我在网上问这个问题的原因:/我不能使用sql developer以外的任何其他软件!不过还是要谢谢你的建议!好的,所以你想说。您的模式1包含350个表,并且希望将这些表与模式2表合并。我的理解正确吗?或者您的意思是说您有一个模式,有350个表名source和target,您希望合并它们。请重新验证,以便我能提供帮助。我有两个模式,每个表(超过350个)彼此相似。只有(每个表的)记录不同。to模式中的一个称为“源”,另一个称为“目标”。如果表结构相同,请查找MERGE语句示例。尝试使用一个表构建,并对其进行测试。如果你有任何问题或将面临一些问题-问他们-我们会帮助你-你并不孤单。但是你必须做这项工作——为了学习和理解你在做什么以及为什么要做。我明天必须试试这个,因为我必须回家,而且我无法访问数据库。提前谢谢你@我想你的第一个查询应该是一个完整的外部连接?可能有一些工具更适合于比较数据库模式(我相信SQLDeveloper有这种能力,尽管我没有使用过),因为您必须检查列、约束等etc@Boneist:是的,当然,这是一个完全的外部连接。我见过男人吗