【问题标题】:MERGE statement unique index/constraint validation per row or per statement?每行或每条语句的 MERGE 语句唯一索引/约束验证?
【发布时间】:2015-02-08 11:40:15
【问题描述】:

假设我有一个带有以下约束的表格:

create table test as (
    select 1 as id, 'a' as name from dual 
    union all 
    select 2, 'b' from dual 
    union all 
    select 3, 'c' from dual
);

create unique index ind on test(name);

alter table test add constraint constr unique (name);

select * from test;

        ID NAME
---------- ----
         1 a   
         2 b   
         3 c   

现在假设我做了以下MERGE:

merge into test t using (
    select 4 as id, 'b' as name from dual 
    union all 
    select 2 as id, null as name from dual 
) s on (s.id = t.id) 
    when matched then update set t.name = s.name
    when not matched then insert(t.id, t.name) values(s.id, s.name)

select * from test;

        ID NAME
---------- ----
         1 a   
         2     
         3 c   
         4 b   

上述MERGE永远会失败吗?如果先UPDATEs,然后INSERTs,则索引/约束在执行过程中不会失效。但是如果先INSERTs,再UPDATEs,索引会暂时失效,语句可能会失败?

谁能详细解释(或指出正确的方向)Oracle RDBMS 如何处理此类问题?另外,使用LOG ERRORS INTO子句的处理方式一样吗?

我问这个问题的主要原因以及为什么我需要一个解决方案:我的 MERGE 语句使用 LOG ERRORS INTO 子句运行了几个小时。错误日志似乎作为一个自主事务工作。在语句完成更新插入之前很久就记录了一些唯一约束错误(基于唯一索引)(其中,我看到序列上升),我不知道为什么(尽管最后,在更新插入之后,应该没有唯一约束无效)。当我查看 ERROR 表时,我看到 ORA-00001:INSERT 操作违反了唯一约束 (XXX.YYY)。我可以将此记录从 ERROR 表插入到主表中,而不会导致唯一约束失败。所以我想知道为什么首先记录错误。

编辑:下面的答案断言,当执行语句时,约束在语句末尾强制执行。我理解并同意(同时我想了解更多关于此类情况下的索引维护的详细信息)。我不明白为什么这个问题仍然没有回答是为什么我有这些 ORA-00001: unique constraint (XXX.YYY) 违反了错误记录,而它们不应该记录。似乎错误记录机制不是以原子方式运行的。

EDIT2:

Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
PL/SQL Release 11.2.0.4.0 - Production
CORE    11.2.0.4.0  Production
TNS for Solaris: Version 11.2.0.4.0 - Production
NLSRTL Version 11.2.0.4.0 - Production

EDIT3: 我玩了一下,能够重现这个错误:

drop table test;

drop table err_test;

create table test as (
    select 1 as id, 'a' as name from dual 
    union all 
    select 2, 'b' from dual 
    union all 
    select 3, 'c' from dual
);

create unique index ind on test(name);

alter table test add constraint constr unique (name);

--select test.rowid, test.* from test;

BEGIN
DBMS_ERRLOG.CREATE_ERROR_LOG (
   dml_table_name            => 'TEST',
   err_log_table_name        => 'ERR_TEST');
END;
/

--truncate table err_test;

select * from err_test;

merge /*+ PARALLEL(t 2) */ into test t using (
    select 4 as id, 'b' as name from dual 
    union all 
    select 2 as id, null as name from dual 
) s on (s.id = t.id) 
    when matched then update set t.name = s.name
    when not matched then insert(t.id, t.name) values(s.id, s.name)
LOG ERRORS INTO ERR_TEST('TEST,ID:'||s.id) REJECT LIMIT UNLIMITED;

select * from err_test;

在最后一个select * from err_test; 我总是得到:ORA-00001: unique constraint (XXX.CONSTR) violated。现在奇怪的是,真正的 MERGE 语句(在生产中)不再在 PARALLEL 中工作,有时我仍然会收到这个错误......

EDIT4: 我标记为已接受的最佳答案,尽管问题本身并没有完全回答。看来这只是Oracle中的一个错误。

【问题讨论】:

  • 您正在运行什么确切数据库版本? Metalink 包含一些关于 MERGE 和 ORA-00001 的错误的文档。
  • Alos,您是否尝试过在不记录错误的情况下运行 MERGE? Metalink 17449815(“对于具有 INSERT 和 UPDATE 分支的 MERGE sql 观察到索引不一致,并且还指定了 ERROR LOGGING(LOG ERRORS INTO 子句)”)可能是相关的。
  • @FrankSchmitt 将其视为错误的好主意。版本是 11.2.0.4.0。我会调查的。我确实尝试在没有 ERROR LOGGING 子句的情况下运行它,但从未遇到任何限制。是生产环境,不能无限期删除子句。
  • 事务的第一个 ACID 属性是原子性。这意味着要么事务成功并且所有已完成的操作都被提交,要么它在某一时刻失败并且所有已完成的都回滚,这意味着它就像什么都没有完成。

标签: sql oracle oracle11g merge-statement


【解决方案1】:

正如其他用户指出的那样,作业的“LOG ERRORS INTO”部分发生在语句执行之后(更新和插入部分),同时检查约束。因此,您可以在约束检查完成之前插入错误。 这就是为什么您会在语句完全完成之前看到插入的错误。

作为对这一观察的回答:

我可以将 ERROR 表中的这条记录插入到主表中,而无需 导致唯一约束失败。所以我想知道为什么会记录错误 首先。

确保您在一个 Merge 语句中包含全部信息。如果您不更新同一语句中的值,而是在插入失败和重试之间发生的另一个语句中更新值,则事情是可以解释的。

(我的意思是USING部分的记录不在同一个语句中。

  • 会话 1:使用 select 4 as id, 'b' as name from dual 合并 (错误被插入到日志中)
  • 会话 2:使用 select 2 as id, null as name from dual 合并 提交确定
  • 会话 3:您重试 插入就可以了

)

如果您可以用一个语句重现错误,那将是一个问题。但是您的环境中有许多会话。请检查您的合并语句的来源。您可能会迟到,或类似情况。

【讨论】:

  • Afaik,没有其他语句在操作该表。从 ERR 表的 ora_err_tag$ 消息中,我可以得出结论,上述 MERGE 语句导致了失败。不过,您有一些有效的观点。我会调查他们。我还更新了我的问题(edit3)。
【解决方案2】:

此合并永远不会失败。

此处通过示例对此进行了解释:Database Concepts - 5. Data Integrity

对于不可延迟的约束(默认):

在不可延迟的约束中,Oracle 数据库从不推迟 约束的有效性检查到事务结束。 相反,数据库会在每个结束时检查约束 语句。 如果违反了约束,则语句滚动 返回。



上面的意思是,约束被检查在整个单个 SQL 语句的末尾,而不是在它们的执行过程中。



下面,在本文档中,您可以找到两个交易示例,它们在“内部”执行期间违反了一些约束规则,但最终它们满足了所有约束,并且是合法的,因为:

... 因为数据库在 语句完成。图 5-4 显示数据库执行 检查约束之前整个 SQL 语句的操作。

最后他们还写道:

本节中的示例说明了约束检查 INSERT 和 UPDATE 语句期间的机制,但数据库使用 所有类型的 DML 语句的机制相同。相同的机制 用于所有类型的约束,而不仅仅是自引用 约束。

【讨论】:

  • 感谢您的指导。请注意,约束只是问题的一部分。唯一索引处理完全不同。看完那份文件后我会回来的。
  • 关于约束,你是绝对正确的:它们在语句的末尾被检查并且不应该引起问题。但我发现很难理解这同样适用于索引。在执行期间维护索引,对于唯一索引,rowid 不被视为“键的一部分”。我刚刚浏览了 Concepts ManualKyte's DB Architecture,但在语句中找不到任何关于索引处理的有用信息。
  • 但是你为什么在更新期间关心索引维护呢?他们清楚地说:你的 SQL 语句永远不会失败,除非在事务执行结束时违反约束。。一个数据库事务必须是 ACID:en.wikipedia.org/wiki/ACID,特别是必须遵守一致性规则——例如,如果有一个约束A+B=100,那么该规则必须独立于事务中内部操作的顺序。无论顺序是'A+B=100'还是'B+A=100',都必须成功,第二种情况'B+A=100'不能失败,因为顺序不对跨度>
  • 是的,我同意。但是去掉上面例子中的唯一约束(只留下唯一索引),语句将在唯一索引上失败,而不是在约束上。如果唯一索引等同于不可延迟的唯一约束,那么可以,但情况似乎并非总是如此:外键只能在唯一约束上创建,而不是索引。此外,基于唯一函数的索引没有等效的唯一约束。所以我很难相信约束检查也适用于索引检查。
  • 这还不是全部。我有 MERGE 语句使用 LOG ERRORS INTO 子句运行了数十个小时。错误日志似乎可以作为autonomous transaction 工作。在语句完成更新插入之前很久就记录了一些唯一约束错误(基于唯一索引)(其中,我看到序列上升),我不知道为什么(尽管最后,在更新插入之后,应该没有唯一约束无效)。这就是我最初的问题和唯一索引维护背后的原因。
猜你喜欢
  • 1970-01-01
  • 2018-07-26
  • 2018-10-12
  • 1970-01-01
  • 2013-05-07
  • 1970-01-01
  • 1970-01-01
  • 2019-02-16
  • 2014-06-29
相关资源
最近更新 更多