Oracle仅显示已更改的列

时间:2019-07-09 17:50:01

标签: sql oracle

我有一个审计表,该审计表的列以“ old_”和“ new_”为前缀,以指示以下旧值和新值:

 ID              <-- Primary Key
 old_cust_name
 new_cust_name
 old_address
 new_address

有人知道随时可用的软件包/过程吗?它可以动态生成代码以生成报告并显示列更改,类似于以下内容:

 Example

         ID     old_name   new_name   old_address  new_address
         101      Andru     Andrew      Main_St      Main_St
         102      Bill      Bill        1st_Av       2nd_Av
         103      jack      jack        High_St      High_St

 Output 

         101   name changed    : Andru  --> Andrew
         102   address changed : 1st_Av --> 2nd_Av
         103   no change

2 个答案:

答案 0 :(得分:1)

您可以将case..when表达式用作

select case 
       when nvl(old_name,new_name||'x') != nvl(new_name,old_name||'x') then 
         ID||'   name changed    : '||old_name||' --> '||new_name
       when nvl(old_address,new_address||'x') != nvl(new_address,old_address||'x') then  
         ID||'   address changed : '||old_address||' --> '||new_address            
       else
         ID||'   no change'
       end as "Output" 
  from t;

Demo

答案 1 :(得分:0)

好吧,因此我很高兴地创建了一个有点复杂的查询,该查询将生成一个SQL语句来比较旧值和新值。它根据Oracle数据字典以及您的规则以“ NEW_”和“ OLD_”开头来确定主键列和列列表。只需在其第一部分中替换您的架构所有者和table_name。

我没有对它进行彻底的测试,以至于我可以放心地说可能没有错误,或者它不会“表现不佳”。但这似乎适用于这个简单的测试用例。

我也从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>

希望有帮助。如果发现有问题,请告诉我。

Francisco。