【问题标题】:Is it possible to perform an upsert that requires filtering in Postgres?是否可以执行需要在 Postgres 中过滤的 upsert?
【发布时间】:2019-04-11 08:41:47
【问题描述】:

我想知道是否可以使用以下语句进行带有过滤的 upsert。也就是说,我是否可以先尝试使用 where 子句进行更新,如果失败,然后插入,而不是相反?我想在 Postgres 中执行此操作。

INSERT ... ON CONFLICT 什么都不做/更新

我确实看到了这个,但它肯定有点复杂 https://dba.stackexchange.com/questions/13468/idiomatic-way-to-implement-upsert-in-postgresql

【问题讨论】:

    标签: postgresql upsert


    【解决方案1】:

    也就是说,我是否可以先尝试使用 where 子句进行更新,如果失败则插入,而不是相反?

    尚不清楚您为什么要这样做。

    UPSERT 的目的是确保数据库只包含具有给定键和一组给定其他列值的行。 Postgres 首先尝试 INSERT,因为当键与重复行冲突时,INSERT 将失败(因此它可以回退到更新冲突行而不是引发异常)。如果 WHERE 子句不匹配,则 UPDATE 不会失败。它将successfully update zero rows。如果您违反约束(例如 CHECK 或 NOT NULL 约束),UPDATE 可能会失败,但不会因为您没有匹配任何行而失败。

    另一方面,如果您的 UPDATE 会更改现有行,那么您的 INSERT 必然会因违反唯一性而失败(因为该行存在)。因此,在这种情况下,首先尝试 INSERT 实际上并不会改变结果。

    可以在 PostgreSQL 的 UPSERT 上挂起一个条件,语法格式为 INSERT... ON CONFLICT DO UPDATE... WHERE...。这将:

    1. 插入您提供的行。
    2. 对于与现有行的每个冲突,评估该行的 WHERE 条件。
    3. 如果满足 WHERE 条件,则更新现有行,否则不执行任何操作。

    我相信这在功能上等同于您所要求的,因为:

    1. 如果该行不存在,Postgres 将插入它。 UPDATE 不会影响它,所以无论如何你的方法都不得不退回到 INSERTing 它。
    2. 如果行存在,但不匹配 WHERE 子句,那么 Postgres 将不做任何事情。我认为您的方法在尝试 INSERT 后要么什么都不做,要么因唯一性约束而失败,但也许您对这种情况有其他想法。
    3. 如果该行存在并且匹配 WHERE 子句,Postgres 和您的方法都会对该行执行 UPDATE。

    【讨论】:

    • 谢谢你的详细回答,你说的有道理。我将继续使用insert ... on conflict do...where 语法,但我意识到我需要在我的表上的一些列上添加一些约束,例如unique 约束,以便它们可以有效地变成键(w/o它们变成主键,这是我试图避免的)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-26
    • 1970-01-01
    • 2020-09-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多