让我们想象一下,我们有一些漂亮,简单的表格:
CREATE TABLE blah (
some_key VARCHAR(32),
some_value VARCHAR(32)
);
出于某种原因,我们不希望在some_key
列上定义主键或唯一索引。
我们现在在并行事务中执行MERGE
语句(即第二个语句在第一个提交之前启动):
MERGE INTO blah blah
USING (SELECT 'some_key' some_key, 'a_value' some_value FROM DUAL) rec
ON (blah.some_key = rec.some_key)
WHEN MATCHED THEN
UPDATE SET blah.some_value = rec.some_value
WHEN NOT MATCHED THEN
INSERT (blah.some_key, blah.some_value) VALUES (rec.some_key, rec.some_value);
和
...
USING (SELECT 'some_key' some_key, 'another_value' some_value FROM DUAL) rec
...
提交后,SELECT
显示以下内容:
| SOME_KEY | SOME_VALUE |
|----------|---------------|
| some_key | a_value |
| some_key | another_value |
我理解这背后的机制(即两个合并声明不是"看到"彼此),但这显然不是理想的结果。
当然,我可以定义一个UNIQUE
密钥。然后第二个问题失败了 - 这也是可以解释的,但在MERGE
声明中有些意外。
有没有办法让MERGE
做一个真实的" MERGE
没有任何重复"键"?
答案 0 :(得分:2)
如果在some_key
上创建唯一约束是有意义的,那么你真的应该创建它。这是完全防止重复密钥的唯一方法。
除了创建唯一约束之外,听起来,你真正想要的是能够序列化合并语句。换句话说,为了保证2个并发用户永远不能同时真正运行两个merge
语句。相反,必须等到另一个完成后,没有其中一个用户的麻烦得到错误。
这是一个想法,实现不涉及锁定您正在合并的整个表。您可以创建一个专门用于序列化整个操作的单独表。然后,您可以使用select ... for update
锁定该表的特定行,以表示您的操作。实际上,这会序列化对merge
语句的访问。然后,在执行merge
之后,您提交事务以允许其他人执行merge
,但只有在更改对其他事务可见之后才会执行。
以下示例脚本为您提供了一个想法:
<强>设置强>
create table operation_lock (
operation_name varchar2(50) not null
)
/
alter table operation_lock
add constraint operation_lock_pk
primary key (operation_name)
/
insert into operation_lock (operation_name) values ('my_merge_operation')
/
commit
/
如何在交易中运行merge
操作
select * from operation_lock
where operation_name = 'my_merge_operation'
for update
/
merge into blah blah...
/
commit
/