【发布时间】: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