【问题标题】:Postgres: how to upsert while ignoring primary key?Postgres:如何在忽略主键的同时进行更新?
【发布时间】:2020-08-24 16:35:48
【问题描述】:

我正在尝试将来自数据不经常更改的源系统的记录存储在数据库中。只想在数据更改时存储新记录。例如,考虑一个具有整数主键和三个字段的 Postgres 表,a,b,c,

id, a, b, c
-----------
1  foo bar baz

如果我们的源系统提供以下更新

(foo, bar, baz)
(foo, bar, baz)
(foo, bar, bazinga)

那么数据库应该只用改变的记录来更新

id, a, b, c
-----------
1  foo bar baz
2  foo bar bazinga

我很难找到一种直接的方法来执行此操作。 这是数据库可以管理的事情还是应用程序必须这样做?

我的第一个想法是一个 upsert。是否可以在仅指定与更改相关的字段的情况下进行更新插入? upsort 可以忽略 pk/ID 字段吗?

另一种方法是根据某些字段计算传入值的哈希值,然后将其存储为附加列。然后新的写入只需要比较哈希字段。

id, a, b, c, hash
-----------------
1  foo bar baz, jl23l8s
2  foo bar bazinga, xl39la2

如果采用这种方式,应用程序应该负责拥有记录还是 DB 可以以某种方式处理?

【问题讨论】:

    标签: python postgresql hash


    【解决方案1】:

    PostgreSQL 可以使用unique 约束和on conflict upsert 来处理这个问题。

    alter table my_table add constraint uq_my_table_a_b_c unique (a, b, c);
    

    然后对于 upsert:

    insert into my_table (a, b, c)
    on conflict on constraint uq_my_table_a_b_c do nothing;
    

    根据您的评论,您希望允许重复的 (a, b, c) 只是不连续的(可能按 id 的顺序)。

    您可以将其用于插入:

    with last_rec as (
      select a, b, c
        from my_table
       order by id desc
       limit 1
    )
    insert into my_table (a, b, c)
     select 'foo', 'bar', 'baz'
     except
     select a, b, c from last_rec;
    

    【讨论】:

    • 实际上 - 这有一个问题。更新的行可能具有旧行的值。我们只是想确保它们之间的行发生了变化。在那个原因中,这会失败吗?因为唯一约束适用于所有行,而不仅仅是最后一行,是吗?
    • @AdamHughes 是的。这将在创建约束时失败。您将需要使用触发器来实现这一点。如果您可以完全控制向数据库中插入记录,则可以使用 CTE 来完成。
    猜你喜欢
    • 1970-01-01
    • 2019-01-10
    • 2021-06-19
    • 1970-01-01
    • 2023-03-27
    • 2017-08-23
    • 1970-01-01
    • 1970-01-01
    • 2012-02-09
    相关资源
    最近更新 更多