【问题标题】:Postgres delete before insert in single transactionPostgres 在单个事务中插入之前删除
【发布时间】:2020-05-08 21:18:05
【问题描述】:

PostgreSQL 数据库:v 9.4.24

create table my_a_b_data ... // with a_uuid, b_uuid, and c columns

注意:my_a_b_data 保留对 a 和 b 表的引用。所以它保留了a和b的uuid。

其中:primary key (a_uuid, b_uuid)

还有一个索引

create unique index my_a_b_data_pkey
    on my_a_b_data (a_uuid, b_uuid);

在Java 类似jdbc的代码中,在单一事务的作用域内:(start() -> [code (delete, insert)] ->commit()]) (org.postgresql:postgresql:42.2.5 driver)

delete from my_a_b_data where b_uuid = 'bbb';
insert into my_a_b_data (a_uuid, b_uuid, c) values ('aaa', 'bbb', null);

我发现插入失败,因为删除还没有删除。所以它失败了,因为它不能被复制。

问:PostgreSQL 中是否存在某种限制,DB 不能在一个事务中执行删除和插入操作,因为 PostgreSQL 不会更新其索引,直到执行删除的提交,因此插入将失败,因为索引中已经存在 id 或 key(我们可能使用的任何内容)?

有什么可能的解决方案?分成两笔交易?

更新:顺序完全相同。当我在 SQL 控制台中单独测试 sql 时。它工作正常。我们使用 JDBI 库 v 5.29。

它看起来像这样:

 @Transaction
 @SqlUpdate("insert into my_a_b_data (...; // similar for the delete
 public abstract void addB() ..

所以在代码中:

this.begin();
this.deleteByB(b_id);
this.addB(a_id, b_id);
this.commit();

【问题讨论】:

  • 在您的insert 声明中应该是b_uuid 而不是b?如果不是,那么这种行为就不足为奇了。如果您的意思是b_uuid,那么如果两个语句都以该顺序运行在单个事务中,则不会发生这种情况。
  • b_uuid。是的。它是对 a 和 b 表的引用/id,它在此表中保留对 a 和 b 的引用。已更新。
  • 列:a_uuid、b_uuid、c: - 值:aaa、bbb、null。 (我想已经修好了)。谢谢
  • 这绝对不是 Postgres 造成的。除非您在两个不同的(并发)事务中运行这两个语句,否则这不会发生。我猜您的方法是同时调用的,因此您在程序中创建了两个不同的(并发)事务。您需要在单个事务中调用这两种方法
  • 我不知道 JDBI,但您可能想检查它是否在单独的事务中运行带有 @Transaction 注释的方法。考虑发布minimal reproducible example

标签: postgresql jdbc transactions postgresql-9.4 jdbi


【解决方案1】:

我在插入重复值时遇到了类似的问题,我通过使用插入和更新而不是删除解决了这个问题。我在 Python 上创建了这个过程,但您也许可以重现它:

  1. 首先,您创建一个临时表,就像您要插入值的目标表一样,不同之处在于该表在提交后被删除。

    CREATE TEMP TABLE temp_my_a_b_data 
    (LIKE public.my_a_b_data INCLUDING DEFAULTS) 
    ON COMMIT DROP;
    
  2. 我创建了一个 CSV(我必须合并不同的数据以输入),其中包含我想在表中输入/插入的值,我使用 COPY 函数将它们插入到 temp_table (temp_my_a_b_data )。

    我在这篇与 Java 和 COPY PostgreSQL - \copy command 相关的帖子中找到了这段代码:

    String query ="COPY tmp from 'E://load.csv' delimiter ','";
    
  3. 使用 INSERT INTO 但带有 ON_CONFLICT 子句,当由于指定的约束而无法完成插入时,您可以决定执行操作,在以下情况下我们进行更新:

    INSERT INTO public.my_a_b_data 
    SELECT * 
    FROM temp_my_a_b_data 
    ON CONFLICT (a_uuid, b_uuid,c) DO UPDATE 
      SET a_uuid = EXCLUDED.a_uuid, 
          b_uuid = EXCLUDED. c = EXCLUDED.c;`
    

注意事项:

我不确定,但您也许可以在不使用前面的步骤、临时表或复制来源的情况下执行第三步。您可以对这些值进行循环:

INSERT INTO public.my_a_b_data VALUES(value1, value2, null) 
ON CONFLICT (a_uuid, b_uuid,c) DO UPDATE 
   SET a_uuid = EXCLUDED.a_uuid, 
       b_uuid = EXCLUDED.b_uuid, c = EXCLUDED.c;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-12
    • 1970-01-01
    • 2013-12-17
    • 1970-01-01
    • 2014-04-10
    相关资源
    最近更新 更多