Хорошо, поэтому для удовольствия я создал несколько запутанный запрос, который сгенерирует оператор SQL для сравнения старых и новых значений.Он вычисляет столбцы первичного ключа и список столбцов на основе словаря данных Oracle и вашего правила, что они начинаются с «NEW_» и «OLD_».Просто замените своего владельца схемы и имя_таблицы в первой части.
Я не проверил ее полностью до такой степени, что могу с уверенностью сказать, что ошибок может не быть или она не будет "плохо себя вести".Но, похоже, это работает для этого простого контрольного примера.
Я также позаимствовал решение Barbaros Özhan, так что спасибо вам за это.
with tab as (
select owner, table_name
from dba_tables t where t.owner = 'FSITJA' and table_name in ('TEST_COMPARE')),
col_list as (
select co.owner,
co.table_name,
cn.column_name new_col_name,
co.column_name old_col_name,
row_number() over (partition by co.owner, co.table_name order by co.column_name) position,
decode(row_number() over (partition by co.owner, co.table_name order by co.column_name),
count(*) over (partition by co.owner, co.table_name),
'Y',
'N') last_col
from dba_tab_columns cn
join dba_tab_columns co on co.owner = cn.owner
and co.table_name = cn.table_name
and regexp_replace(co.column_name, '^OLD_(.*)$', '\1') = regexp_replace(cn.column_name, '^NEW_(.*)$', '\1')
where (co.owner, co.table_name) in (select owner, table_name from tab)
and regexp_like(cn.column_name, '^NEW_')),
pk_col_list as (
select c.owner,
c.table_name,
cc.column_name,
cc.position,
decode(cc.position, max(cc.position) over (partition by c.owner, c.table_name), 'Y', 'N') last_col
from dba_constraints c
join dba_cons_columns cc on c.owner = cc.owner
and c.table_name = cc.table_name
and c.constraint_name = cc.constraint_name
where (c.owner, c.table_name) in (select owner, table_name from tab)
and c.constraint_type = 'P'
) --
select replace(replace(extract(xmlconcat(xmlelement("xml_part", sql_select),
xmlelement("xml_part", xml_pkcol_list),
xmlelement("xml_part", xml_col_list),
xmlelement("xml_part", from_t)), '/xml_part/text()'),
chr(38)||'apos;', ''''),
chr(38)||'gt;', '>') full_sql
from (select pl.owner,
pl.table_name,
'SELECT ' || chr(10) sql_select,
extract(xmlagg(xmlelement("column_name", ' ' || pl.column_name || ',' || chr(10)) order by pl.position), '/column_name/text()') xml_pkcol_list
from pk_col_list pl
group by pl.owner,
pl.table_name,
'SELECT ' || chr(10)) part1
join (select cl.owner,
cl.table_name,
extract(xmlagg(xmlelement("column_name", ' CASE WHEN nvl(' || cl.old_col_name || ', ' || cl.new_col_name ||
'||''1'') != nvl(' || cl.new_col_name || ', ' || cl.old_col_name || '||''1'')' || chr(10) ||
' THEN ''' || regexp_replace(cl.old_col_name, '^OLD_(.*)$', '\1') ||
' changed: '' || nvl(to_char(' || cl.old_col_name || '), ''''''NULL'''''') || '' --> '' || nvl(to_char(' || cl.new_col_name ||
'), ''''''NULL'''''')' || chr(10) ||
' ELSE ''' || regexp_replace(cl.old_col_name, '^OLD_(.*)$', '\1') ||
' unchanged'' END AS ' || regexp_replace(cl.old_col_name, '^OLD_(.*)$', '\1') ||
decode(cl.last_col, 'N', ',') || chr(10)
) order by cl.position), '/column_name/text()') xml_col_list,
' FROM ' || cl.owner || '.' || cl.table_name || ' t;' from_t
from col_list cl
group by cl.owner,
cl.table_name,
' FROM ' || cl.owner || '.' || cl.table_name || ' t;') part2 on part1.owner = part2.owner and part1.table_name = part2.table_name;
И вот пример выполнения:
FSITJA@db01 2019-07-10 13:21:56> create table fsitja.test_compare (id number primary key,
2 new_name varchar2(10),
3 old_name varchar2(10),
4 new_salary number,
5 old_salary number,
6 new_address varchar2(30),
7 old_address varchar2(30));
Table created.
FSITJA@db01 2019-07-10 13:21:56> insert into fsitja.test_compare values (1, 'John', 'James', 1000, 1000, '123 That Road', '332 This Avenue');
1 row created.
FSITJA@db01 2019-07-10 13:21:56> insert into fsitja.test_compare values (2, 'Pat', 'Pat', 1200, 2000, 'None', 'None');
1 row created.
FSITJA@db01 2019-07-10 13:21:56> insert into fsitja.test_compare values (3, 'Jack', null, 5000, 5000, 'None', 'None');
1 row created.
FSITJA@db01 2019-07-10 13:21:56> insert into fsitja.test_compare values (4, 'Dean', 'Dean', null, 900, 'None', 'DIFF ADDRESS');
1 row created.
FSITJA@db01 2019-07-10 13:21:56> insert into fsitja.test_compare values (5, 'Anne', 'Anne', null, null, null, null);
1 row created.
FSITJA@db01 2019-07-10 13:21:56> commit;
Commit complete.
FSITJA@db01 2019-07-10 13:21:56>
FSITJA@db01 2019-07-10 13:21:56> with tab as (
2 select owner, table_name
3 from dba_tables t where t.owner = 'FSITJA' and table_name in ('TEST_COMPARE')),
4 col_list as (
5 select co.owner,
6 co.table_name,
7 cn.column_name new_col_name,
8 co.column_name old_col_name,
9 row_number() over (partition by co.owner, co.table_name order by co.column_name) position,
10 decode(row_number() over (partition by co.owner, co.table_name order by co.column_name),
11 count(*) over (partition by co.owner, co.table_name),
12 'Y',
13 'N') last_col
14 from dba_tab_columns cn
15 join dba_tab_columns co on co.owner = cn.owner
16 and co.table_name = cn.table_name
17 and regexp_replace(co.column_name, '^OLD_(.*)$', '\1') = regexp_replace(cn.column_name, '^NEW_(.*)$', '\1')
18 where (co.owner, co.table_name) in (select owner, table_name from tab)
19 and regexp_like(cn.column_name, '^NEW_')),
20 pk_col_list as (
21 select c.owner,
22 c.table_name,
23 cc.column_name,
24 cc.position,
25 decode(cc.position, max(cc.position) over (partition by c.owner, c.table_name), 'Y', 'N') last_col
26 from dba_constraints c
27 join dba_cons_columns cc on c.owner = cc.owner
28 and c.table_name = cc.table_name
29 and c.constraint_name = cc.constraint_name
30 where (c.owner, c.table_name) in (select owner, table_name from tab)
31 and c.constraint_type = 'P'
32 ) --
33 select replace(replace(extract(xmlconcat(xmlelement("xml_part", sql_select),
34 xmlelement("xml_part", xml_pkcol_list),
35 xmlelement("xml_part", xml_col_list),
36 xmlelement("xml_part", from_t)), '/xml_part/text()'),
37 chr(38)||'apos;', ''''),
38 chr(38)||'gt;', '>') full_sql
39 from (select pl.owner,
40 pl.table_name,
41 'SELECT ' || chr(10) sql_select,
42 extract(xmlagg(xmlelement("column_name", ' ' || pl.column_name || ',' || chr(10)) order by pl.position), '/column_name/text()') xml_pkcol_list
43 from pk_col_list pl
44 group by pl.owner,
45 pl.table_name,
46 'SELECT ' || chr(10)) part1
47 join (select cl.owner,
48 cl.table_name,
49 extract(xmlagg(xmlelement("column_name", ' CASE WHEN nvl(' || cl.old_col_name || ', ' || cl.new_col_name ||
50 '||''1'') != nvl(' || cl.new_col_name || ', ' || cl.old_col_name || '||''1'')' || chr(10) ||
51 ' THEN ''' || regexp_replace(cl.old_col_name, '^OLD_(.*)$', '\1') ||
52 ' changed: '' || nvl(to_char(' || cl.old_col_name || '), ''''''NULL'''''') || '' --> '' || nvl(to_char(' || cl.new_col_name ||
53 '), ''''''NULL'''''')' || chr(10) ||
54 ' ELSE ''' || regexp_replace(cl.old_col_name, '^OLD_(.*)$', '\1') ||
55 ' unchanged'' END AS ' || regexp_replace(cl.old_col_name, '^OLD_(.*)$', '\1') ||
56 decode(cl.last_col, 'N', ',') || chr(10)
57 ) order by cl.position), '/column_name/text()') xml_col_list,
58 ' FROM ' || cl.owner || '.' || cl.table_name || ' t;' from_t
59 from col_list cl
60 group by cl.owner,
61 cl.table_name,
62 ' FROM ' || cl.owner || '.' || cl.table_name || ' t;') part2 on part1.owner = part2.owner and part1.table_name = part2.table_name;
FULL_SQL
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------
SELECT
ID,
CASE WHEN nvl(OLD_ADDRESS, NEW_ADDRESS||'1') != nvl(NEW_ADDRESS, OLD_ADDRESS||'1')
THEN 'ADDRESS changed: ' || nvl(to_char(OLD_ADDRESS), '''NULL''') || ' --> ' || nvl(to_char(NEW_ADDRESS), '''NULL''')
ELSE 'ADDRESS unchanged' END AS ADDRESS,
CASE WHEN nvl(OLD_NAME, NEW_NAME||'1') != nvl(NEW_NAME, OLD_NAME||'1')
THEN 'NAME changed: ' || nvl(to_char(OLD_NAME), '''NULL''') || ' --> ' || nvl(to_char(NEW_NAME), '''NULL''')
ELSE 'NAME unchanged' END AS NAME,
CASE WHEN nvl(OLD_SALARY, NEW_SALARY||'1') != nvl(NEW_SALARY, OLD_SALARY||'1')
THEN 'SALARY changed: ' || nvl(to_char(OLD_SALARY), '''NULL''') || ' --> ' || nvl(to_char(NEW_SALARY), '''NULL''')
ELSE 'SALARY unchanged' END AS SALARY
FROM FSITJA.TEST_COMPARE t;
FSITJA@db01 2019-07-10 13:21:59> SELECT
2 ID,
3 CASE WHEN nvl(OLD_ADDRESS, NEW_ADDRESS||'1') != nvl(NEW_ADDRESS, OLD_ADDRESS||'1')
4 THEN 'ADDRESS changed: ' || nvl(to_char(OLD_ADDRESS), '''NULL''') || ' --> ' || nvl(to_char(NEW_ADDRESS), '''NULL''')
5 ELSE 'ADDRESS unchanged' END AS ADDRESS,
6 CASE WHEN nvl(OLD_NAME, NEW_NAME||'1') != nvl(NEW_NAME, OLD_NAME||'1')
7 THEN 'NAME changed: ' || nvl(to_char(OLD_NAME), '''NULL''') || ' --> ' || nvl(to_char(NEW_NAME), '''NULL''')
8 ELSE 'NAME unchanged' END AS NAME,
9 CASE WHEN nvl(OLD_SALARY, NEW_SALARY||'1') != nvl(NEW_SALARY, OLD_SALARY||'1')
10 THEN 'SALARY changed: ' || nvl(to_char(OLD_SALARY), '''NULL''') || ' --> ' || nvl(to_char(NEW_SALARY), '''NULL''')
11 ELSE 'SALARY unchanged' END AS SALARY
12 FROM FSITJA.TEST_COMPARE t;
ID ADDRESS NAME SALARY
---- -------------------------------------------------- ------------------------------ ------------------------------
1 ADDRESS changed: 332 This Avenue --> 123 That Road NAME changed: James --> John SALARY unchanged
2 ADDRESS unchanged NAME unchanged SALARY changed: 2000 --> 1200
3 ADDRESS unchanged NAME changed: 'NULL' --> Jack SALARY unchanged
4 ADDRESS changed: DIFF ADDRESS --> None NAME unchanged SALARY changed: 900 --> 'NULL'
5 ADDRESS unchanged NAME unchanged SALARY unchanged
FSITJA@db01 2019-07-10 13:22:06>
Надеюсь, это поможет.Пожалуйста, дайте мне знать, если вы обнаружите в нем проблемы.
Франциско.