sql >> データベース >  >> RDS >> Oracle

テーブルの構造が不明な2つのテーブルを比較するためのplsqlプロシージャ

    temp1を想定 およびtemp2 テーブルには同じ列があり、EXECUTE IMMEDIATEを使用すると簡単に実行できます 、およびOracleシステムテーブルを参照する方法を知っているALL_TABLES およびALL_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) ここで基本的なテクニックが説明されています。




    1. Oracleで連結文字列を生成する高速な方法

    2. SQLサブクエリの結果で「LIKE」を使用する

    3. トリガーがテーブルを認識しない(NEW.valuesのコンテンツを複数の行に分割して別のテーブルに挿入するトリガー)

    4. 単一テーブルのエンティティ生成