plsql过程比较两个表,其中表的结构未知

时间:2013-10-08 08:15:29

标签: oracle stored-procedures plsql procedures

所以问题是这样的:

我有一个包含两列的表,即源查询和目标查询。 每行包含来自源端和目标端的映射的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用于源表和目标表用于另一个具有两列的表,一列是源查询,另一列是目标查询,此表中的每一行都有不同的查询查询下次创建这些表时,列数和名称可能会更改。

1 个答案:

答案 0 :(得分:1)

假设您的temp1temp2表具有相同的列,使用EXECUTE IMMEDIATE时很容易做到,并且知道如何浏览Oracle系统表ALL_TABLES和{ {1}}。

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

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

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

我这样做很有意思,所以我会尝试为它编写一个小代码,假设您的PK是单列DATA_TYPE

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

这受到Search All Fields In All Tables For A Specific Value (Oracle)的启发,其中解释了基本的技术。