有没有办法以原子方式执行 SQL 合并?

Is there a way to execute an SQL merge atomically?

我正在寻找一种方法,以非锁定方式将值元组与随机 UUID 连接起来,并且不会因并发约束而导致事务失败。

我需要编辑的 table 包含几个应该由 UUID 描述的值。在此示例中,table 被命名为 foo 并声明两个字符串列 barqux,它们指向单个字段 uuid(bar, qux) 在整个 table 中必须是唯一的。 UUID 本质上是唯一的。

我想知道 SQL(使用 Oracle 12c)是否能够自动执行如下操作:

MERGE INTO foo a
  USING (SELECT bar, qux FROM foo b
  ON b.bar = 'a' and b.qux = 'b'
WHEN NOT MATCHED THEN INSERT (a.bar, a.qux, a.uuid)
  VALUES ('a', 'b', 'some-uuid');

SELECT uuid FROM foo WHERE bar = 'a' and qux = 'b'; 

作为我的数据库查询的结果,我希望元组 (bar, qux) 与随机 UUID 连接。对于任何并发事务,此 UUID 必须相同,我不希望竞争请求因并发插入另一个(随机)UUID 而失败。

作为背景:这些插入是相当长的 运行 交易的一部分,这些交易在很大程度上彼此独立,但具有此共享标识符 table,其中值不得不一致。许多编程语言都提供 CAS,这就是我在这种情况下想要的,但我不知道 SQL.

中的类似功能

作为一个想法,我想知道是否允许 脏读 (未提交的读取隔离级别)是否是一种解决方案,但我不知道合并语句是否在这种情况下是原子的并且对其他事务可见。(这在 Oracle 中是不可能的。)数据库通过 JDBC 访问,但可能来自多个 VM 节点。

您不能在 long-running 事务中执行此操作,因为插入只有在事务提交后才可见。

你需要做的是从应用层打开一个新的事务,然后到MERGE或UPSERT。

这样,MERGE/UPSERT 原子性由您发出的辅助交易保证。这样,一旦提交了次级事务,如果您在 READ_COMMITTED 中是 运行,那么 long-running 的事务将看到更改,但不是 SERIALIZABLE。

您可以将 MERGE 和 SELECT 语句封装在 PL/SQL 函数中,该函数使用 AUTONOMOUS_TRANSACTION 编译指示定义。

如果因为另一个会话刚插入相同的 UUID 而导致违反唯一约束,您可以在函数中捕获异常,并且只需 select 和 return UUID。

这样 MERGE 语句只在短时间内锁定记录(只要函数需要执行)并且您不会推断您的应用程序当前事务,因为该函数在单独的事务中运行并且在违反唯一约束的情况下进行错误处理。