Postgres 在单个事务中先删除后插入

ses*_*ses 5 postgresql transactions jdbc jdbi postgresql-9.4

PostgreSQL 数据库:v 9.4.24

\n\n
create table my_a_b_data ... // with a_uuid, b_uuid, and c columns\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n

其中:primary key (a_uuid, b_uuid)

\n\n

还有一个索引

\n\n
create unique index my_a_b_data_pkey\n    on my_a_b_data (a_uuid, b_uuid);\n
Run Code Online (Sandbox Code Playgroud)\n\n

在类似于 Java jdbc 的代码中,在一个事务范围内(start() -> [code (delete, insert)] ->commit()]):( org.postgresql:postgresql:42.2.5 driver)

\n\n
delete from my_a_b_data where b_uuid = \'bbb\';\ninsert into my_a_b_data (a_uuid, b_uuid, c) values (\'aaa\', \'bbb\', null);\n
Run Code Online (Sandbox Code Playgroud)\n\n

发现插入失败,因为删除还没有删除。所以它失败了,因为它无法复制。

\n\n

问: DB 不能在一个事务中执行删除和插入操作,这是否是PostgreSQL中的某种限制,因为 PostgreSQL 在执行删除提交之前不会更新其索引,因此插入会失败,因为 id 或 key(无论我们使用什么)已经存在于索引中?

\n\n

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

\n\n

更新:顺序完全相同。当我在SQL控制台中单独测试sql时。效果很好。我们使用 JDBI 库 v 5.29。

\n\n

它看起来像这样:

\n\n
 @Transaction\n @SqlUpdate("insert into my_a_b_data (...; // similar for the delete\n public abstract void addB() ..\n
Run Code Online (Sandbox Code Playgroud)\n\n

所以在代码中:

\n\n
this.begin();\nthis.deleteByB(b_id);\nthis.addB(a_id, b_id);\nthis.commit();\n
Run Code Online (Sandbox Code Playgroud)\n

小智 1

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

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

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

    我在这篇文章中发现了与 Java 和 COPY PostgreSQL - \copy 命令相关的代码:

    String query ="COPY tmp from 'E://load.csv' delimiter ','";
    
    Run Code Online (Sandbox Code Playgroud)
  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;`
    
    Run Code Online (Sandbox Code Playgroud)

注意事项:

我不确定,但您也许可以在不使用前面的步骤、临时表或副本的情况下执行第三步。您可以只循环遍历这些值:

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;
Run Code Online (Sandbox Code Playgroud)