Странное поведение MERGE при использовании статического / постоянного предложения ON

CREATE TABLE test (c1 NUMBER(10) NOT NULL);

exec DBMS_ERRLOG.CREATE_ERROR_LOG ( 'test', 'err_test');

Я хочу вставить в таблицу test некоторые значения из другой таблицы и регистрировать ошибки. Я также хочу регистрировать идентификаторы записей, которые не могут быть вставлены в таблицу err_test. В псевдокоде это приведет к чему-то вроде этого:

INSERT INTO test(c1)
SELECT some_nr AS special_nr FROM some_table_which_has_an_unique_id_for_each_number
LOG ERRORS INTO err_test('Could not insert record with id:' || some_table.id /* where id is the id number of the special_nr */ ) REJECT LIMIT UNLIMITED;

Выше, очевидно, не работает, потому что src.id неизвестно Но я могу выполнить вышеуказанную вставку с помощью оператора слияния, который действует как оператор вставки:

MERGE INTO test trg
USING (SELECT NULL AS special_nr, 17 AS id FROM DUAL ) src
ON      (1 = 2) -- force INSERTS (!!) Not using INSERT statement because in err log extra data is needed.
WHEN NOT MATCHED THEN 
        INSERT  (trg.c1)
        VALUES  (src.special_nr)
LOG ERRORS INTO err_test('Could not insert record with id:' || src.id) REJECT LIMIT UNLIMITED;

После выполнения вышеизложенного ошибка не регистрируется в err_table, а возвращается пользователю:

    ORA-01489: result of string concatenation is too long
    ORA-01400: cannot insert NULL into ("SCV_DPN"."TEST"."C1")

Строка слишком длинная? Затем я пытаюсь без конкатенации в err_mesg $:

MERGE INTO test trg
USING (SELECT NULL AS special_nr, 17 AS id FROM DUAL ) src
ON      (1 = 2) -- force INSERTS (!!) Not using INSERT statement because in err log extra data is needed.
WHEN NOT MATCHED THEN 
        INSERT  (trg.c1)
        VALUES  (src.special_nr)
LOG ERRORS INTO err_test(src.id) REJECT LIMIT UNLIMITED;

Auch:

    ORA-38908: internal error occurred during DML Error Logging
    ORA-01024: invalid datatype in OCI call
    ORA-01400: cannot insert NULL into ("SCV_DPN"."TEST"."C1")      

Становится еще лучше:

MERGE INTO test trg
USING (SELECT NULL AS special_nr, 17 AS id FROM DUAL ) src
ON      (1 = 2) -- force INSERTS (!!) Not using INSERT statement because in err log extra data is needed.
WHEN NOT MATCHED THEN 
        INSERT  (trg.c1)
        VALUES  (src.special_nr)
LOG ERRORS INTO err_test('Could not insert record with id:' || nvl(src.id, -1)) REJECT LIMIT UNLIMITED;

Теперь меня выгнали:

    ORA-03113: end-of-file on communication channel
    Process ID: 25352
    Session ID: 171 Serial number: 979  

Что тут происходит? Кажется 1 = 2 виновник, потому что

MERGE INTO test trg
USING (SELECT NULL AS special_nr, 17 AS id FROM DUAL ) src
ON      (src.id IS NULL) -- force INSERTS (!!) Not using INSERT statement because in err log extra data is needed.
WHEN NOT MATCHED THEN 
        INSERT  (trg.c1)
        VALUES  (src.special_nr)
LOG ERRORS INTO err_test('Could not insert record with id:' || src.id) REJECT LIMIT UNLIMITED;

работает как надо и (!!) регистрирует ошибку в err_table

    ORA_ERR_NUMBER$     ORA_ERR_MESG$                                                   ORA_ERR_ROWID$  ORA_ERR_OPTYP$  ORA_ERR_TAG$                            C1
    1400                ORA-01400: cannot insert NULL into ("SCV_DPN"."TEST"."C1")                      I               Could not insert record with id:17          

Теперь это работает без проблем.

Может кто-нибудь объяснить мне, что здесь происходит, и / или также дать лучший подход для решения вышеуказанной проблемы, когда дополнительная информация из исходной таблицы должна регистрироваться в ORA_ERR_MESG$ поле.

Примечание: согласно документации я могу использовать 1 = 2: Чтобы вставить все исходные строки в таблицу, вы можете использовать предикат постоянного фильтра в условии предложения ON. Примером предиката с постоянным фильтром является ON (0=1).

1 ответ

Я считаю, что вы пропустили одного возможного виновника: включение значения строки в параметр тега. Я не верю, что эта функциональность предназначена для обеспечения функциональности, для которой вы пытаетесь ее использовать. Хотя я не могу найти никакой документации на этот счет, простое изменение первоначального merge (тот, с ошибкой конкатенации), кажется, подтверждает это:

MERGE INTO test trg
USING (SELECT NULL AS special_nr, 17 AS id FROM DUAL ) src
ON      (1 = 2) -- force INSERTS (!!) Not using INSERT statement because in err log extra data is needed.
WHEN NOT MATCHED THEN 
        INSERT  (trg.c1)
        VALUES  (src.special_nr)
LOG ERRORS INTO err_test('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx') REJECT LIMIT UNLIMITED;

Этот SQL успешен, несмотря на то, что тег превышает длину объединенного значения, которое было изначально предоставлено.

Чтобы получить более подробное ведение журнала, которое вы хотите, я бы предложил использовать курсор для цикла, содержащего вставку. Это может быть немного медленнее, но это позволит вам регистрировать столько деталей, сколько вы захотите в случае ошибки.


Документация, кажется, поддерживает этот вывод, хотя это скорее подразумевается, чем заявлено. При описании "простого выражения", разрешенного для регистрации ошибок, в нем говорится: "Выражение может быть либо текстовым литералом, числовым литералом, либо общим выражением SQL, таким как переменная связывания".

Другие вопросы по тегам