是否可以执行需要在Postgres中进行过滤的upsert?

时间:2018-11-07 20:49:02

标签: postgresql upsert

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

  

插入...在冲突时不做/更新

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

1 个答案:

答案 0 :(得分:1)

  

也就是说,我可以先尝试使用where子句进行更新(如果失败),然后再插入,而不是相反吗?

目前尚不清楚为什么要这么做。

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

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

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

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

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

  1. 如果该行不存在,Postgres将对其进行插入。 UPDATE不会影响它,因此您的方法将不得不退回到INSERTing它。
  2. 如果该行存在,但与WHERE子句不匹配,则Postgres将不执行任何操作。我认为您的方法在尝试插入后将无法执行任何操作,或者由于唯一性约束而失败,但是对于这种情况,您可能还需要考虑其他事情。
  3. 如果该行存在并与WHERE子句匹配,则Postgres和您的方法都将对该行进行UPDATE。