1

選択クエリと挿入クエリが varchar として格納されているテーブルがあります。選択クエリを実行し、プロシージャを使用して挿入クエリを使用して選択クエリの結果を挿入する必要があります。現在、選択クエリをvarcharのテーブルに収集する即時および一括収集を実行しています。

この後、vartable の各行に移動し、値を取得して挿入クエリに入れます。いくつかのテーブルに対して、選択クエリと挿入クエリのペアがいくつかあります。したがって、これは動的に行う必要があります。私の質問は、選択クエリの結果を保存するより良い方法はありますか? varchar のテーブルを使用する以外に? 選択クエリの結果セットには数百万のレコードが含まれる可能性があり、これが問題を引き起こす可能性があるためです。ネストされたテーブル型を使用し、それにEXTENDを使用すると問題は解決しますか?

PROCEDURE SEL_INS
AS
  CURSOR C
  IS
    SELECT 
      SELEQRY SELQRY,
      INSQUERY INSERTQRY,
      cols COLS
      FROM TAB1;
      selqry  VARCHAR2(1000);
      insqry VARCHAR2(1000);
      tab1 vartable:=vartable();
      cols   NUMBER;

 BEGIN
   tab1:=vartable(NULL);
     FOR X    IN C
      LOOP
          selqry:= X.SELQRY;
          cols:=X.COLS;
  EXECUTE immediate selqry bulk collect INTO tab1;
 -- select statement is concatenated before executing. so that each index has one record    
 --with values separated by commas 
   --- a sample column in tab1 will have values like (abc,abc1,abc2)
     FOR i IN 1..tab1.count
     LOOP
       insqry :=X.INSERTQRY; 
       --- insert query will have values like insert into tab2   values('abc,'abc1','abc2')

         EXECUTE immediate insqry;

     END LOOP;
   END LOOP;


  END SEL_INS;

vartable は varchars2(4000) 型のテーブルです

4

2 に答える 2

3

As given in comments you should try to rewrite your statement as INSERT INTO ... SELECT .... Let's assume this is not possible for whatever reason. In this case you can use procedure as below:

PROCEDURE SEL_INS AS

    CURSOR C IS
    SELECT SELEQRY, INSQUERY, COLS
    FROM TAB1;

    selqry  VARCHAR2(1000);
    insqry VARCHAR2(1000);
    tab1 vartable;
    cols   NUMBER;

    cur INTEGER;
    res INTEGER;
    col_cnt INTEGER;
    desctab DBMS_SQL.DESC_TAB;
    i INTEGER;

BEGIN

    FOR aQuery IN C LOOP
        tab1 := vartable();

        cur := DBMS_SQL.OPEN_CURSOR;
        DBMS_SQL.PARSE(cur, aQuery.SELEQRY, DBMS_SQL.NATIVE);
        DBMS_SQL.DESCRIBE_COLUMNS(cur, col_cnt, desctab)
        FOR i IN 1..col_cnt LOOP
            DBMS_SQL.DEFINE_COLUMN(cur, i, desctab(i).COL_NAME, 2000);
        END LOOP;
        res := DBMS_SQL.EXECUTE_AND_FETCH(cur, TRUE);
        FOR i IN 1..col_cnt LOOP
            tab1.EXTEND;
            DBMS_SQL.COLUMN_VALUE(cur, i, tab1(tab1.LAST));
        END LOOP;
        DBMS_SQL.CLOSE_CURSOR(cur);

        -- ... do whatever with tab1(xyz) -> otherwise this procedure would be an overkill

        cur := DBMS_SQL.OPEN_CURSOR;
        DBMS_SQL.PARSE(cur, aQuery.INSQUERY, DBMS_SQL.NATIVE);
        i := tab1.FIRST;
        WHILE i IS NOT NULL LOOP
            DBMS_SQL.BIND_VARIABLE(cur, ':b'||i, tab1(i));
            i := tab1.NEXT(i);
        END LOOP;
        res := DBMS_SQL.EXECUTE(cur);
        DBMS_SQL.CLOSE_CURSOR(cur);

    END LOOP;   

END;

Note, this procedure presumes that all columns are VARCHAR2 data type (with max. length of 2000 chars). If you have other data types, then line DBMS_SQL.DEFINE_COLUMN(cur, i, desctab(i).COL_NAME, 2000); must be extended like IF desctab(c).col_type = 1 THEN ...

Also, note DBMS_SQL.EXECUTE_AND_FETCH will fail unless your select returns exactly one row. If your query may return more than just one row you have to use

DBMS_SQL.EXECUTE(cur);
WHILE (DBMS_SQL.FETCH_ROWS(cur) > 0) LOOP
   ...
END LOOP;

See Oracle Built-in Data Types to get the code number of each data type.

于 2017-01-03T12:48:47.577 に答える
1

実行するSQLステートメントを格納するテーブルに格納されているデータを変更できると仮定すると、これは、格納されたデータを次のように表示する例です。

INSERT INTO sel_ins (selqry, insquery)
SELECT 'select col1, col2, col3 from table1' selqry, 'insert into other_table1 (col1, col2, col3)') insqry FROM dual
UNION ALL
SELECT 'select col1, col2 from table2' selqry, 'insert into other_table2 (col1, col2)') insqry FROM dual
UNION ALL
SELECT 'select col1, col2, col3 from table3 where col4 = ''fred''' selqry, 'insert into other_table3 (col1, col2, col3)') insqry FROM dual;

そうすることで、挿入を行う手順がはるかに簡単になりました。

PROCEDURE sel_ins IS
  CURSOR ins_sel_cur IS
    SELECT seleqry selqry, insquery insqry, cols cols
    FROM   tab1;
  v_selqry tab1.seleqry%TYPE;
  v_insqry tab1.insquery%TYPE;
BEGIN
  FOR ins_sel_rec IN ins_sel_cur
  LOOP
    EXECUTE IMMEDIATE ins_sel_rec.v_insqry || CHR(10) || ins_sel_rec.v_selqry;
  END LOOP;
END;
/

この方法では、テーブルから大量のデータを取得してメモリに保存するのではなく、そのデータを取得して別のテーブルに行ごとに追加するだけです。単一の DML ですべての作業を行っています。ステートメント (ビルダーが、ドライブの下部ではなく、壁を構築しているポイントに配送する必要があるすべてのレンガを含むトラックを取得し、各レンガを 1 つずつ取りに行くことに相当します)。これにより、読みやすく、保守しやすいことは言うまでもなく、物事が大幅に高速化されるはずです。

列を別々に保存したい場合、例えば:

INSERT INTO sel_ins (selqry, insquery, cols)
SELECT 'select <COLS> from table1' selqry, 'insert into other_table1 (<COLS>)') insqry, 'col1, col2, col3' cols FROM dual
UNION ALL
SELECT 'select <COLS> from table2' selqry, 'insert into other_table2 (<COLS>)') insqry, 'col1, col2' cols FROM dual
UNION ALL
SELECT 'select <COLS> from table3 where col4 = ''fred''' selqry, 'insert into other_table3 (<COLS>)') insqry, 'col1, col2, col3' cols FROM dual;

次に、即時実行は次のようになります。

execute immediate replace(ins_sel_rec.v_insqry, '<COLS>', ins_sel_rec.cols) ||
                  chr(10) ||
                  replace(ins_sel_rec.v_selqry, '<COLS>', ins_sel_rec.cols);
于 2017-01-04T12:34:00.897 に答える