Странное поведение 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, таким как переменная связывания".