有没有办法知道在MERGE中执行了什么操作?

Is there a way to know what operation was performed in MERGE?

提问人:kaqqao 提问时间:11/17/2023 最后编辑:kaqqao 更新时间:11/17/2023 访问量:61

问:

使用 jOOQ 和 PostgreSQL,有没有办法知道为 MERGE、INSERT 或 UPDATE 执行了什么操作?

例如,使用 jOOQ 文档中的示例:

create.mergeInto(AUTHOR)
      .using(create.selectOne())
      // Suppose the last name is unique (natural key) in my case
      .on(AUTHOR.LAST_NAME.eq("Hitchcock"))
      .whenMatchedThenUpdate()
      .set(AUTHOR.FIRST_NAME, "John")
      .whenNotMatchedThenInsert(AUTHOR.LAST_NAME)
      .values("Hitchcock")
      .execute();

理想情况下,如果可能的话,我会同时获得生成的行和执行的操作。

注意:不是这个问题的重复。这是特定于 SQL Server 的。我的是特定于 PostgreSQL 和 jOOQ 的。

java sql postgresql jooq

评论

0赞 Gurkan İlleez 11/17/2023
它返回受影响的行数,对吗?如果您想知道哪些字段已更新或类似内容,则需要保留该表的历史记录。
0赞 kaqqao 11/17/2023
@Gurkanİlleez我不需要知道到底更新了什么,只需要知道它是更新的还是在合并期间新插入的。
0赞 Gurkan İlleez 11/17/2023
如果你在数据库中需要它,那么你可以使用触发器。如果代码中需要它,则很可能需要发送选择查询。因为在执行插入或更新时,数据库不会返回该信息。

答:

2赞 Zegarek 11/17/2023 #1

PostgreSQL MERGE 不能将子句用于其基础 AND 语句,因此您只剩下受影响的行总数,而没有任何迹象表明它们是如何受到影响的。医生:RETURNINGINSERTUPDATE

成功完成后,命令将返回表单的命令标记MERGE

MERGE total_count

是更改的总行数(无论是插入、更新还是删除)。total_count

没有带有 的子句。的 操作 ,不能包含 或 子句。RETURNINGMERGEINSERTUPDATEDELETERETURNINGWITH

您可以使用较旧的更新插入语法 INSERT...ON CONFLICT DO UPDATE 能够使用子句,并在其中检查受影响行的 xmax 系统列:插入的行将始终为零,更新的行将具有非零。示例RETURNING

jOOQ 同时提供 .onConflict() 和 .returningResult()(在 3.11 之前只是 .returning())。有问题的部分似乎是隐藏系统列的暴露,您可能无法立即引用。如果情况仍然如此,您将需要一个普通的 SQL 字段在此处找到)。

如果到达太麻烦了,并且您不介意乱扔您的架构,则可以设置一个反射器列,而不是尝试使用隐藏的 .或者,如果您确定插入批处理从不包含冲突解决值,请计算最终使用这些值的值。xmaxxmax

1赞 Lukas Eder 11/17/2023 #2

您始终可以这样做:

create.mergeInto(AUTHOR)
      .using(create.selectOne())
      // Suppose the last name is unique (natural key) in my case
      .on(AUTHOR.LAST_NAME.eq("Hitchcock"))
      .whenMatchedThenUpdate()
      .set(AUTHOR.FIRST_NAME, "John")
      .set(AUTHOR.LAST_ACTION, "UPDATE")
      .whenNotMatchedThenInsert(AUTHOR.LAST_NAME, AUTHOR.LAST_ACTION)
      .values("Hitchcock", "INSERT")
      .execute();

然后再次读取记录,因为 PostgreSQL 不支持标准 SQL 语法或等效语法。MERGE<data change delta table>

您还可以在触发器中自动执行上述操作。这样的触发器还可以使用 NOTIFY 命令向客户端指示它对每一行执行的操作,而不是存储在表中。LAST_ACTION

评论

0赞 Zegarek 11/18/2023
在两步解决方法中,除非您将这两个操作都包装在一个事务中,否则您可能会读取与您刚才的内容不对应的内容,而是与您和后续之间的其他人执行的操作相对应。与此相对应的是,我将该附加列称为反射器,因为您可以在语句级别从中反弹操作类型信息,因此不需要其他操作。LAST_ACTIONMERGEDMERGESELECTINSERT...ON CONFLICT
0赞 Zegarek 11/18/2023
+ 或 + 侧信道反馈还要求您识别语句,以便客户可以从广播通知中分辨出他们的内容(我怀疑将他们想要的频道传递给触发器以直接打开他们是否可行)。为此,客户端保存其交易信息是有意义的,这些信息可以与触发器中可见的系统列(cmin/cmax/xmin/xmax,对应于客户端的上下文)相关联。triggertabletriggernotifylistennotify