1

所以问题是这样的:

我有一个包含两列的表,即源查询和目标查询。从源端和目标端映射的 sql 查询,我们需要构建一个协调程序,该程序为每行获取这两个值并在那里生成输出并将其存储在临时表中,例如 temp1 和 temp2,然后比较这两个临时表的结果。我通过创建两个表然后通过两个游标批量获取来做到这一点,并且使用两个表的减集运算符完成比较,然后我们从那里得到不同的行。

现在棘手的部分来了,我们要做的是检查那些具有不同值的行并输出有变化的列的名称,并输出源端值(temp1)和目标端值(temp2)。

如果我之前知道表的结构,那么硬编码是一种到达那里的方法,但是由于表temp1temp2动态创建的,因此我无法理解这种情况,我的意思是如何获得列名和这两个值使用动态循环遍历行并检查值在哪里发生变化的过程,然后输出这两个值和列名。

帮我 !如果您为此提供代码,那将非常有帮助。

样本数据集

SOURCE 
PK  COLUMN1 COLUMN2 COLUMN3 COLUMN4 
2   NAME2   VALUE2  3       4 
1   NAME1   VALUE1  2       3 
3   NAME3   VALUE3  4       5 

TARGET 
PK  COLUMN1 COLUMN2 COLUMN3 COLUMN4 
1   NAME1   VALUE1  2       3 
2   NAME2   VALUE2  4       4 
3   NAME3   VALUE3  4       5 

现在

SELECT * FROM SOURCE MINUS SELECT * FROM TARGET 

PK  COLUMN1 COLUMN2 COLUMN3 COLUMN4 

2   NAME2   VALUE2  3       4 

SELECT * FROM TARGET MINUS SELECT * FROM SOURCE 

PK  COLUMN1 COLUMN2 COLUMN3 COLUMN4 

2   NAME2 VALUE2    4       4 

我们可以看到column3值从 3 变为 4。

所以我们需要的是这样的

COLUMN_NAME OLD_VALUE NEW_VALUE 

COLUMN3     3         4

表源和目标是从一个过程创建的,该过程将源表的 sql 和目标表用于另一个具有两列的表,一个是源查询,另一个是目标查询,并且该表中的每一行都有一个不同的查询查询列和名称可以在下次创建这些表时更改。

4

1 回答 1

1

假设您temp1temp2表具有相同的列,当您使用时很容易做到EXECUTE IMMEDIATE,并且知道如何浏览到 Oracle 系统表ALL_TABLESALL_TAB_COLUMNS.

由于我不知道表有多少列temp,因此想法是比较(与您最初MINUS的想法)列连接的结果。请注意,您不能以相同的方式连接所有内容(例如日期),所以我展示了如何获得DATA_TYPE.

获得上述结果后,您可以手动查看更改的列。如果我有时间,我将添加有关更改的列的部分:

  • 如果您有 PK,那么我们可以使用它来了解更改的行并在列上再次循环;
  • 如果没有PK,可能会变得更棘手......

我这样做很有趣,所以我会尝试为它编写一个小代码,假设你的 PK 是一个名为PK

create or replace procedure compare_tables(t1 in varchar2, t2 in varchar2)
is
    v_qry          varchar2(10000);
    TYPE T_MY_LIST IS TABLE OF VARCHAR2(32000);
    v_cols         T_MY_LIST;  -- list of columns
    v_types        T_MY_LIST;  -- list of columns' type
    v_cmp_cols     T_MY_LIST;  -- list of distinct
    v_col_t1_t2    T_MY_LIST;  -- t1 minus t2 - value of lines
    v_pk_t1_t2     T_MY_LIST;  -- associated PKs in t1 minus t2
    v_col_t2_t1    T_MY_LIST;  -- t2 minus t1 - value of lines
    v_pk_t2_t1     T_MY_LIST;  -- associated PKs in t2 minus t1
    TYPE T_Y_ IS TABLE OF VARCHAR2(32000) index by varchar2(1000);
    v_s                                            varchar2(1000); -- for indexing
    v_t1_t2        T_Y_; -- list of distinct lines from t1 - t2 /indexed by PK
    v_t2_t1        T_Y_; -- list of distinct lines from t2 - t1 /indexed by PK
begin
    -- the below assumes all tables have a PK called simply "PK".
    v_qry:='PK, ';
    execute immediate ' select COLUMN_NAME, DATA_TYPE '
                      ||' from ALL_TAB_COLUMNS where TABLE_NAME=upper('''||t1||''')' 
            bulk collect into v_cols, v_types;
    -- building query with list of columns:
    FOR I in 1..v_cols.count loop -- dbms_output.put_line(v_cols(i)||'.'||v_types(i));
        v_qry := v_qry||v_cols(i)||'||';
    end loop;
    v_qry := v_qry||'''''';
    execute immediate ' select '||v_qry||' from '||t1||' minus select '||v_qry||' from '||t2
            bulk collect into v_pk_t1_t2, v_col_t1_t2;
    execute immediate ' select '||v_qry||' from '||t2||' minus select '||v_qry||' from '||t1
            bulk collect into v_pk_t2_t1, v_col_t2_t1;

    -- build indexed structures that will help compare lines brought by "minus" queries
    FOR I in 1..v_pk_t1_t2.count loop
        v_t1_t2(v_pk_t1_t2(i)):=v_col_t1_t2(i);
    end loop;
    FOR I in 1..v_pk_t2_t1.count loop
        v_t2_t1(v_pk_t2_t1(i)):=v_col_t2_t1(i);
    end loop;

    v_s := v_t1_t2.FIRST;          -- Get first element of array
    WHILE v_s IS NOT NULL LOOP
        if (v_t2_t1.exists(v_s)) then
            -- distinct rows on same PK
            DBMS_Output.PUT_LINE (v_s || ' -> ' || v_t1_t2(v_s));

            -- loop on each column joined on PK:
            FOR i in 1..v_cols.count
            loop
                v_qry:= 'select '''||v_cols(i)||':''||'||t1||'.'||v_cols(i)||'||''<>''||'||t2||'.'||v_cols(i)
                      ||'  from '||t1||','||t2
                      ||' where '||t1||'.PK='||t2||'.PK'
                      ||'   and '||t1||'.PK='||v_s
                      ||'   and '||t1||'.'||v_cols(i)||'<>'||t2||'.'||v_cols(i)
                ;
                --DBMS_Output.PUT_LINE (v_qry);
                execute immediate v_qry bulk collect into v_cmp_cols;
                FOR j in 1..v_cmp_cols.count loop
                    DBMS_Output.PUT_LINE (v_cmp_cols(j));
                end loop;
            end loop;
        else 
            DBMS_Output.PUT_LINE (v_s || ' not in ' || t2);            
        end if;
      v_s := v_t1_t2.NEXT(v_s);    -- Get next element of array
    END LOOP;
    v_s := v_t2_t1.FIRST;          -- Get first
    WHILE v_s IS NOT NULL LOOP
        if (not v_t1_t2.exists(v_s)) then
            DBMS_Output.PUT_LINE (v_s || ' not in ' || t1);            
        end if;
      v_s := v_t2_t1.NEXT(v_s);    -- Get next
    END LOOP;
end compare_tables;
/

测试数据:

create table temp1 (PK number,
  COLUMN1 varchar2(10), 
  COLUMN2 varchar2(10),
  COLUMN3 varchar2(10),
  COLUMN4 varchar2(10)
  );

create table temp2 (PK number,
  COLUMN1 varchar2(10), 
  COLUMN2 varchar2(10),
  COLUMN3 varchar2(10),
  COLUMN4 varchar2(10)
  );
delete temp1;
insert into temp1 
          select 1, 'a', 'a', 'bb', 'cc' from dual
union all select 2, 'a', 'a', 'bb', 'cc' from dual
union all select 3, 'a', 'a', 'bb', 'cc' from dual
union all select 4, 'a', 'a', 'bb', 'cc' from dual
;
insert into temp2 
          select 1, 'a', 'a', 'bb', 'cc' from dual
union all select 2, 'a', 'a', 'b', 'cc'  from dual
union all select 3, 'a', 'a', 'bb', 'cc' from dual
;


begin
    compare_tables('temp1','temp2');
end;
/

结果:

2 -> 2aabbcc
COLUMN3:bb<>b
4 not in temp2

这受到了在所有表中搜索所有字段以获取特定值 (Oracle)的启发,其中解释了基本技术。

于 2016-06-29T14:28:26.997 回答