Как объединить две одинаковые схемы базы данных в PL / SQL? - PullRequest
0 голосов
/ 27 июня 2018

База данных-схема (Source и target) очень велика (каждая имеет более 350 таблиц). У меня есть задача как-то объединить эти две таблицы в одну. Сами данные (что в таблицах) должны быть перенесены. Я должен быть осторожным, чтобы не было двойных записей для первичных ключей до или во время слияния схем. Кто-нибудь когда-либо уже делал это и сможет предоставить мне свое решение, или кто-нибудь может помочь мне найти подход к этой задаче? Все мои подходы провалились, и мой консультант просто попросил меня обратиться за помощью онлайн: /

К моему подходу: Я пытался использовать таблицу "all_constraints", чтобы получить все pks из моей базы данных.

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 в общем) Я действительно понятия не имею, как / что мне делать дальше: /

Было бы замечательно, если бы кто-нибудь мог мне помочь:)

Спасибо за чтение этого бесконечного поста и за всех, кто готов помочь заранее: D

(P.s .: Пожалуйста, игнорируйте любые грамматические ошибки. Я являюсь носителем немецкого языка.)

Вот ссылка на ERD базы данных: https://ufile.io/9tdoj

проверка на вирусы: https://www.virustotal.com/#/file/dbe5f418115e50313a2268fb33a924cc8cb57a43bc85b3bbf5f6a571b184627e/detection

Ответы [ 3 ]

0 голосов
/ 27 июня 2018

1-й из всех, для 350 таблиц, скорее всего, потребуется dynamic SQL.

  1. Объявите CURSOR или COLLECTION - table of VARCHAR2 со всеми именами таблиц.
  2. Объявите строковую переменную для построения dynamic SQL.
  3. loop через весь список имен таблиц и для каждой таблицы генерирует строку, которая будет выполняться как SQL с помощью команды EXECUTE IMMEDIATE.
  4. Динамический SQL, который будет построен, должен вставлять значения из исходной таблицы в целевую таблицу. Если PK уже существует, в целевой таблице следует проверить поле, которое представляет дату последнего обновления, и если в исходной таблице оно больше, чем в целевой таблице, то выполнить обновление в целевой таблице, иначе ничего не делать.
0 голосов
/ 27 июня 2018

Как и обещал помочь в моем комментарии, я подготовил динамический код, который вы можете попробовать получить данные merged с исходной и целевой таблицами. Логика следующая:

Шаг 1: Получить все имена таблиц из схемы SOURCE. В приведенном ниже запросе вы можете заменить имя схемы (владельца) соответственно. Для тестирования я взял только 1 таблицу, поэтому при ее запуске удалите условие фильтрации имен таблиц.

Шаг 2: Получить имена столбцов с ограничениями для таблицы. Это используется для подготовки предложения ON, которое впоследствии будет использоваться для оператора MERGE.

Шаг 3: Получить неограниченные имена столбцов для таблицы. Это будет использоваться в предложении UPDATE при использовании MERGE.

Шаг 4. Подготовьте список insert, если данные не соответствуют условию ON оператора MERGE.

Прочитайте мои встроенные комментарии, чтобы понять каждый шаг.

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: я проверил это с таблицей с 2 ​​столбцами, из которых у меня было ограничение уникального ключа. DDL таблицы как показано ниже:

В конце я хотел бы, чтобы вы могли понять мой код (вы нуб) и реализовать нечто подобное, если вышеперечисленное не отвечает вашим требованиям.

 CREATE TABLE TEST
       (    COL2 NUMBER, 
            COLUMN1 VARCHAR2(20 BYTE), 
            CONSTRAINT TEST_UK1 UNIQUE (COLUMN1)  
       ) ;
0 голосов
/ 27 июня 2018

О, дорогой! Обычно такой вопрос можно было бы быстро закрыть как "слишком широкий", но мы должны поддержать жертв злых советников!

Что касается усилий, мне понадобится полная неделя для опытного эксперта, а также двухдневная проверка качества для опытного инженера по обеспечению качества.

Прежде всего, такое сложное слияние данных не сработает с первой попытки. Это означает, что вам понадобятся тестовые копии обеих схем, которые можно легко перестроить. И вам нужно место, чтобы попробовать это. Обычно это делается с помощью экспорта обеих схем и пустой базы данных разработчиков.

Далее, вам нужно, чтобы обе схемы были достаточно близко, чтобы иметь возможность сравнивать данные. Я бы сделал это с помощью импорта файлов экспорта, упомянутых выше. Если имена схем совпадают, переименуйте их во время импорта.

Далее, я бы дважды проверил, если структура действительно идентична, с запросами вроде

 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;
 /
...