0

そのため、比較のためにデータを非正規化するツールを作成して、正規化されたデータの精度をテストしている最中です。これを行っている間、私は通常行うこと(カーソルを使用し、挿入/更新をループする)よりもこのツールの新しいテクニックを学ぶことを検討していたので、試してみたい2つのアイテムに出会いました。それはバルクコレクションとマージステートメントでした. 私の問題は、一括コレクションを利用する最善の方法を見つけるのに苦労していることです。

編集:

わかりましたので、一括収集に関して私の問題/解決策を見つけました。実際、それは私がそれを取得していた方法でした。forall ステートメントを使用する代わりに、 for に変更し、その下にループを追加しました。これは、より多くのバグの発見につながります。indx に格納されている値を呼び出そうとした方法が間違っていたので、修正しました。現在、私が抱えていると思われる唯一の問題は、タイトルに記載されているエラーです。何らかの理由でマージで、挿入で使用しようとした最初の値で次のエラーがスローされます。

PL/SQL: ORA-38101: INSERT VALUES 句の列が無効です: "TMI"."MACHINE_INTERVAL_ID" ORA-06550: 行 92、列 7:

だから私が今知りたいのは、なぜこのエラーが発生するのかということです。挿入値が無効であるという概念を理解しています。しかし、なぜそうなのかは完全にはわかりません。

これは、問題のマージ ステートメントです。

MERGE INTO TEST_MACHINE_INTERVAL TMI
      USING (SELECT * FROM TEST_MACHINE_INTERVAL) OTMI
      ON (TMI.MACHINE_INTERVAL_ID = OTMI.MACHINE_INTERVAL_ID)
      WHEN MATCHED THEN
        UPDATE SET  TMI.MACHINE_INTERVAL_ID = OTMI.MACHINE_INTERVAL_ID, TMI.START_DATE_TIME = OTMI.START_DATE_TIME,
                    TMI.INTERVAL_DURATION = OTMI.INTERVAL_DURATION,  TMI.CALC_END_TIME = OTMI.CALC_END_TIME, 
                    TMI.MACHINE_NAME = OTMI.MACHINE_NAME, TMI.SITE_NAME = OTMI.SITE_NAME, 
                    TMI.OPERATOR_INSTANCE = OTMI.OPERATOR_INSTANCE, TMI.OPERATOR_INSTANCE2 = OTMI.OPERATOR_INSTANCE2,
                    TMI.OPERATOR_INSTANCE3 = OTMI.OPERATOR_INSTANCE3, TMI.SHIFT_NAME = OTMI.SHIFT_NAME,
                    TMI.INTERVAL_CATEGORY = OTMI.INTERVAL_CATEGORY, TMI.NTP_CATEGORY_NAME = OTMI.NTP_CATEGORY_NAME,
                    TMI.MACHINE_MODE = OTMI.MACHINE_MODE, TMI.JOB_LOAD_STATE_NAME = OTMI.JOB_LOAD_STATE_NAME,
                    TMI.RAW_SOURCE_MSG_TYPE = OTMI.RAW_SOURCE_MSG_TYPE  
       WHEN NOT MATCHED THEN
        INSERT (TMI.MACHINE_INTERVAL_ID, TMI.START_DATE_TIME, TMI.INTERVAL_DURATION, TMI.CALC_END_TIME, 
                TMI.MACHINE_NAME, TMI.SITE_NAME, TMI.OPERATOR_INSTANCE, TMI.OPERATOR_INSTANCE2,
                TMI.OPERATOR_INSTANCE3, TMI.SHIFT_NAME, TMI.INTERVAL_CATEGORY, TMI.NTP_CATEGORY_NAME,
                TMI.MACHINE_MODE, TMI.JOB_LOAD_STATE_NAME, TMI.RAW_SOURCE_MSG_TYPE )
        VALUES (MACHINE_INTERVAL_ID, START_DATE_TIME, INTERVAL_DURATION, CALC_END_TIME, 
                MACHINE_NAME, SITE_NAME, OPERATOR_INSTANCE, OPERATOR_INSTANCE2, OPERATOR_INSTANCE3,
                SHIFT_NAME, INTERVAL_CATEGORY, NTP_CATEGORY_NAME, MACHINE_MODE,
                JOB_LOAD_STATE_NAME, RAW_SOURCE_MSG_TYPE);

以下は、新しく変更したコードの完全なバージョンです。

-- Denormaliztion of machine_interval Table.
-- Is used to take all intervals from interval_table and convert it from 
-- foreign keys to corresponding names.
DECLARE

START_DATE_TIME TIMESTAMP(6) WITH TIME ZONE;
CALC_END_TIME TIMESTAMP(6) WITH TIME ZONE;
MACHINE_NAME VARCHAR2(256);
SITE_NAME VARCHAR2(256);
OPERATOR_INSTANCE VARCHAR2(256);
OPERATOR_INSTANCE2 VARCHAR2(256);
OPERATOR_INSTANCE3 VARCHAR2(256);
SHIFT_NAME VARCHAR2(256);
INTERVAL_CATEGORY VARCHAR2(256);
NPT_CATEGORY_NAME VARCHAR2(256);
MACHINE_MODE VARCHAR2(256);
JOB_LOAD_STATE_NAME VARCHAR2(256);
RAW_SOURCE_MSG_TYPE VARCHAR2(256);
INTERVAL_DURATION NUMBER;
MACHINE_INTERVAL_ID NUMBER;

--step one: Get all the intervals and store them into a cursor
CURSOR INTERVAL_CUR IS 
SELECT * 
FROM MACHINE_INTERVAL 
ORDER BY START_DATE_TIME ASC;

TYPE TOTAL_MACHINE_INTERVALS IS
TABLE OF interval_cur%rowtype
INDEX BY PLS_INTEGER;

MACHINE_INTERVAL_ROW TOTAL_MACHINE_INTERVALS;

BEGIN 
--step two: Make sure Test_Machine_interval is empty.
  DELETE FROM TEST_MACHINE_INTERVAL;

  OPEN INTERVAL_CUR;
  LOOP
    FETCH INTERVAL_CUR BULK COLLECT INTO MACHINE_INTERVAL_ROW LIMIT 100;
--step three: Loop through all the intervals. 
    FOR INDX IN 1..MACHINE_INTERVAL_ROW.COUNT 
    LOOP
--step four: Gather all datavalues needed to populate test_machine_interval. 

      MACHINE_INTERVAL_ID := MACHINE_INTERVAL_ROW(indx).MACHINE_INTERVAL_ID;
      START_DATE_TIME := MACHINE_INTERVAL_ROW(indx).START_DATE_TIME;
      CALC_END_TIME := MACHINE_INTERVAL_ROW(indx).CALC_END_TIME;
      INTERVAL_DURATION := MACHINE_INTERVAL_ROW(indx).INTERVAL_DURATION;
      INTERVAL_CATEGORY := MACHINE_INTERVAL_ROW(indx).INTERVAL_CATEGORY;
      RAW_SOURCE_MSG_TYPE := MACHINE_INTERVAL_ROW(indx).RAW_SOURCE_MSG_TYPE;

      SELECT M.MACHINE_NAME INTO MACHINE_NAME 
      FROM MACHINE M  
      WHERE MACHINE_ID = MACHINE_INTERVAL_ROW(indx).MACHINE_ID; 

      SELECT S.SITE_NAME INTO SITE_NAME 
      FROM SITE S  
      LEFT OUTER JOIN MACHINE M ON M.SITE_ID = S.SITE_ID 
      WHERE M.MACHINE_ID = MACHINE_INTERVAL_ROW(indx).MACHINE_ID;  

      SELECT O.OPERATOR_NAME INTO OPERATOR_INSTANCE 
      FROM OPERATOR_INSTANCE OI  
      LEFT OUTER JOIN OPERATOR O ON OI.OPERATOR_ID = O.OPERATOR_ID
      WHERE OI.OPERATOR_INSTANCE_ID = MACHINE_INTERVAL_ROW(indx).OPERATOR_INSTANCE_ID; 

      SELECT O.OPERATOR_NAME INTO OPERATOR_INSTANCE2 
      FROM OPERATOR_INSTANCE OI  
      LEFT OUTER JOIN OPERATOR O ON OI.OPERATOR_ID = O.OPERATOR_ID
      WHERE OI.OPERATOR_INSTANCE_ID = MACHINE_INTERVAL_ROW(indx).OPERATOR_INSTANCE_ID_2; 

      SELECT O.OPERATOR_NAME INTO OPERATOR_INSTANCE3 
      FROM OPERATOR_INSTANCE OI  
      LEFT OUTER JOIN OPERATOR O ON OI.OPERATOR_ID = O.OPERATOR_ID
      WHERE OI.OPERATOR_INSTANCE_ID = MACHINE_INTERVAL_ROW(indx).OPERATOR_INSTANCE_ID_3; 

      SELECT NPT_CATEGORY_NAME INTO NPT_CATEGORY_NAME 
      FROM NPT_CATEGORY 
      WHERE NPT_CATEGORY_ID = MACHINE_INTERVAL_ROW(indx).NPT_CATEGORY_ID;

      SELECT S.SHIFT_NAME INTO SHIFT_NAME
      FROM SHIFTS S  
      LEFT OUTER JOIN SHIFT_TBL STBL ON S.SHIFT_ID = STBL.SHIFT_NAME_FK 
      WHERE STBL.SHIFT_ID_PK = MACHINE_INTERVAL_ROW(indx).SHIFT_ID; 

      SELECT MACHINE_MODE_NAME INTO MACHINE_MODE 
      FROM MACHINE_MODE MM 
      WHERE MM.MACHINE_MODE_ID = MACHINE_INTERVAL_ROW(indx).MACHINE_MODE_ID;

      SELECT JLS.JOB_LOAD_STATE_NAME INTO JOB_LOAD_STATE_NAME 
      FROM JOB_LOAD_STATE JLS 
      WHERE JLS.JOB_LOAD_STATE_ID = MACHINE_INTERVAL_ROW(indx).JOB_LOAD_STATE_ID;

    --step five: merge record into test_machine_interval.
      MERGE INTO TEST_MACHINE_INTERVAL TMI
      USING (SELECT * FROM TEST_MACHINE_INTERVAL) OTMI
      ON (TMI.MACHINE_INTERVAL_ID = OTMI.MACHINE_INTERVAL_ID)
      WHEN MATCHED THEN
        UPDATE SET  TMI.MACHINE_INTERVAL_ID = OTMI.MACHINE_INTERVAL_ID, TMI.START_DATE_TIME = OTMI.START_DATE_TIME,
                    TMI.INTERVAL_DURATION = OTMI.INTERVAL_DURATION,  TMI.CALC_END_TIME = OTMI.CALC_END_TIME, 
                    TMI.MACHINE_NAME = OTMI.MACHINE_NAME, TMI.SITE_NAME = OTMI.SITE_NAME, 
                    TMI.OPERATOR_INSTANCE = OTMI.OPERATOR_INSTANCE, TMI.OPERATOR_INSTANCE2 = OTMI.OPERATOR_INSTANCE2,
                    TMI.OPERATOR_INSTANCE3 = OTMI.OPERATOR_INSTANCE3, TMI.SHIFT_NAME = OTMI.SHIFT_NAME,
                    TMI.INTERVAL_CATEGORY = OTMI.INTERVAL_CATEGORY, TMI.NTP_CATEGORY_NAME = OTMI.NTP_CATEGORY_NAME,
                    TMI.MACHINE_MODE = OTMI.MACHINE_MODE, TMI.JOB_LOAD_STATE_NAME = OTMI.JOB_LOAD_STATE_NAME,
                    TMI.RAW_SOURCE_MSG_TYPE = OTMI.RAW_SOURCE_MSG_TYPE  
       WHEN NOT MATCHED THEN
        INSERT (TMI.MACHINE_INTERVAL_ID, TMI.START_DATE_TIME, TMI.INTERVAL_DURATION, TMI.CALC_END_TIME, 
                TMI.MACHINE_NAME, TMI.SITE_NAME, TMI.OPERATOR_INSTANCE, TMI.OPERATOR_INSTANCE2,
                TMI.OPERATOR_INSTANCE3, TMI.SHIFT_NAME, TMI.INTERVAL_CATEGORY, TMI.NTP_CATEGORY_NAME,
                TMI.MACHINE_MODE, TMI.JOB_LOAD_STATE_NAME, TMI.RAW_SOURCE_MSG_TYPE )
        VALUES (MACHINE_INTERVAL_ID, START_DATE_TIME, INTERVAL_DURATION, CALC_END_TIME, 
                MACHINE_NAME, SITE_NAME, OPERATOR_INSTANCE, OPERATOR_INSTANCE2, OPERATOR_INSTANCE3,
                SHIFT_NAME, INTERVAL_CATEGORY, NTP_CATEGORY_NAME, MACHINE_MODE,
                JOB_LOAD_STATE_NAME, RAW_SOURCE_MSG_TYPE);

    /*
      EXECUTE IMMEDIATE 'INSERT INTO TEST_MACHINE_INTERVAL
                                           (MACHINE_INTERVAL_ID, START_DATE_TIME, INTERVAL_DURATION, CALC_END_TIME, 
                                            MACHINE_NAME, SITE_NAME, OPERATOR_INSTANCE, OPERATOR_INSTANCE2,
                                            OPERATOR_INSTANCE3, SHIFT_NAME, INTERVAL_CATEGORY, NTP_CATEGORY_NAME,
                                            MACHINE_MODE,JOB_LOAD_STATE_NAME,RAW_SOURCE_MSG_TYPE )
                                     VALUES(:1, :2, :3, :4, :5, :6, :7, :8, :9, :10, :11, :12, :13, :14, :15)' 
                                     USING  MACHINE_INTERVAL_ID, START_DATE_TIME, INTERVAL_DURATION,
                                            CALC_END_TIME, MACHINE_NAME, SITE_NAME, 
                                            OPERATOR_INSTANCE, OPERATOR_INSTANCE2, OPERATOR_INSTANCE3,
                                            SHIFT_NAME, INTERVAL_CATEGORY, NTP_CATEGORY_NAME,
                                            MACHINE_MODE,JOB_LOAD_STATE_NAME,RAW_SOURCE_MSG_TYPE;
     */
    END LOOP;
  EXIT WHEN MACHINE_INTERVAL_ROW.COUNT = 0; 
END LOOP;
END;

上記のコードに表示されているように、バルク コレクションを取得しようとしている方法に問題があることは 75% 確信しています。だから私の質問は次のとおりです。データのマージで使用するために、バルクコレクションから値を取得するにはどうすればよいですか?

そして、提案やコメントは大歓迎です。ありがとうございました。

4

1 に答える 1

1
  • を使用する場合FORALLは、コレクション全体を渡す 1 つの SQL ステートメントに従う必要があります。コレクション内の要素を単純に反復したい場合は、FORループを使用します。
  • コレクションの n 番目の要素を参照するための構文は、 ですcollection_name(index).column_name

したがって、コレクション内の要素を 1 つずつ反復処理する場合は、次のようなものが必要です。

FOR indx IN MACHINE_INTERVAL_ROW.FIRST..MACHINE_INTERVAL_ROW.COUNT 
LOOP
  MACHINE_INTERVAL_ID := machine_interval_row(indx).MACHINE_INTERVAL_ID;
  START_DATE_TIME     := machine_interval_row(indx).START_DATE_TIME;

  <<more code>>
END LOOP;

ただし、コードをリファクタリングする場合、ローカル変数MACHINE_INTERVAL_IDを使用するだけでなく、machine_interval_row(indx).MACHINE_INTERVAL_ID. また、これらすべてのテーブルを結合して必要なローカル変数を設定するSELECT1 つのステートメントを記述するのではなく、それぞれが単一の行を返す半ダースの個別のステートメントを実行している理由もわかりません。SELECT

あなたMERGEも問題になるでしょう-ソースと宛先の両方がMERGE同じテーブルであることは意味がありません-Oracleが安定した行のセットを生成できなかったというエラーが発生することを期待していますそのステートメントを実行しようとした場合。クエリのソースを、入力したすべてのローカル変数を選択したものに対するクエリに変更できますDUAL。つまり、

  MERGE INTO TEST_MACHINE_INTERVAL TMI
  USING (SELECT machine_interval_row(indx).MACHINE_INTERVAL_ID,
                machine_interval_row(indx).START_DATE_TIME
           FROM dual) OTMI
  ON (TMI.MACHIN_INTERVAL_ID = OTMI.MACHINE_INTERVAL_ID)

TEST_MACHINE_INTERVALただし、 が空で開始する場合は、 を使用せず、 を使用MERGEせず、プルしたいすべてのデータをプルする をBULK COLLECT記述するだけの方がよいように思えます。INSERT ... SELECT何かのようなもの

INSERT INTO test_machine_interval( machine_interval_id,
                                   start_date_time,
                                   <<more columns>> )
  SELECT machine_interval_id,
         last_value(start_date_time) over (partition by machine_interval_id
                                               order by start_date_time asc
                                           rows between unbounded preceding 
                                                    and unbounded following ) last_start_date_time,
         <<more columns>>
    FROM machine_interval
于 2012-05-17T20:37:24.090 に答える