INSERT INTO .. SELECT可能导致竞争状况?

时间:2019-07-08 08:37:36

标签: postgresql

INSERT INTO A
SELECT * FROM B WHERE timestamp > (SELECT max(timestamp) FROM A);

或者,写不同的话:

WITH selection AS
    (SELECT * FROM B WHERE timestamp > (SELECT max(timestamp) FROM A))
INSERT INTO A SELECT * FROM selection;

如果这些查询同时运行多次,是否有可能导致A中的行重复?

Postgres如何处理这些查询?是一个还是多个?

如果它是多个查询(找到max(timestamp)[1],选择[2]然后插入[3]),我可以想象这将导致重复的行。

如果正确,将其包装在BEGIN / END(事务)中会有所帮助吗?

2 个答案:

答案 0 :(得分:2)

是的,这可能导致重复的值。

从语句开始的时间点开始,单个语句就可以看到所有表中数据的一致视图。

将单个语句包装到事务中并不会改变(无论所涉及的子查询数量如何,单个语句始终作为原子语句执行)。

该语句将永远不会看到其他事务中未提交的数据(这是为什么您最终可能会有重复值的根本原因)。

唯一的避免重复值的安全方法是在该列上创建唯一约束(或索引)。在这种情况下,如果该值已经存在,则INSERT将导致错误。

如果要避免该错误,请使用insert ... on conflict

答案 1 :(得分:0)

这取决于数据库中设置的隔离级别。 来自postgres documentation

默认情况下,此设置为可重复读取,这意味着每个查询将基于事务首次尝试读取数据的时间获取输出。如果在任何一个写入之前读取了2个查询,那么您将在这些表中获得重复的数据。

如果要避免重复输入,则有一些选择。

  1. 尝试使用隔离级别可序列化
  2. 在表B中的A字段上应用唯一索引。时间戳不是一个有力的竞争者,因为您可能合法地拥有两行具有相同时间戳的行。表A的id可能是个不错的选择。
  3. 执行此类查询之前,请在应用程序级别锁定。