使用静态/常量 ON 子句时出现奇怪的 MERGE 行为

Posted

技术标签:

【中文标题】使用静态/常量 ON 子句时出现奇怪的 MERGE 行为【英文标题】:Strange MERGE behaviour when using static/constant ON clause 【发布时间】:2014-09-18 13:10:44 【问题描述】:
CREATE TABLE test (c1 NUMBER(10) NOT NULL);

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

我想从另一个表中插入一些值到表test 中,并记录错误。我还想记录无法插入到 err_test 表中的记录的 ID。在伪代码中,这将导致如下内容:

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;

哎呀:

    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$ 字段中。

注意:根据documentation我可以使用1 = 2要将所有源行插入表中,您可以在 ON 子句条件中使用常量过滤谓词。一个常量过滤谓词的示例是 ON (0=1)。

【问题讨论】:

【参考方案1】:

我相信您错过了一个可能的罪魁祸首:在 tag 参数中包含行值。我不认为此功能旨在提供您尝试使用它的功能。虽然我找不到任何相关的文档,但对最初的 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 成功,尽管标签超出了最初提供的连接值的长度。

为了获得您想要的更详细的日志记录,我建议使用包含插入的游标 for 循环。它可能会稍微慢一些,但它可以让您在出现错误时记录尽可能多的详细信息。


documentation 似乎支持这一结论,尽管它比直接陈述更为隐含。在描述允许记录错误的“简单表达式”时,它指出:“表达式可以是文本文字、数字文字或通用 SQL 表达式,例如绑定变量。”

【讨论】:

是的,你是对的。我故意排除了你的例子,因为那不是我所追求的,但它没有抛出错误。也请考虑我在原帖中的说明。 恕我直言,以上是“通用SQL表达式”。无论如何,使用ON(src.id IS NULL) 似乎可行。 我建议不要使用您当前的解决方案。它非常接近未记录的行为,如果未来的版本破坏了此代码,我一点也不感到惊讶。我似乎很清楚,从一些尝试导致的奇怪错误来看,这不是 LOG ERRORS 子句的预期用法。

以上是关于使用静态/常量 ON 子句时出现奇怪的 MERGE 行为的主要内容,如果未能解决你的问题,请参考以下文章

关于在left join的on子句中限制左边表的取值时出现非期望的结果

Swig:包装全局静态常量时出现语法错误

Merge:探讨on子句和when not match子句的陷阱

在 Python 中安装 Psyco 时出现奇怪的错误

在代码中设置自动布局约束时出现奇怪的问题

merge into on条件能有Null判断嘛