【问题标题】:Upsert error (On Conflict Do Update) pointing to duplicate constrained values指向重复约束值的 Upsert 错误(On Conflict Do Update)
【发布时间】:2017-02-07 14:33:10
【问题描述】:

当我尝试在 FROM 语句中使用多个源时,我在 Postgres 9.5 中遇到了 ON CONFLICT DO UPDATE 的问题。

工作代码示例:

    INSERT INTO new.bookmonographs  (citavi_id, abstract, createdon, edition, title, year)
SELECT "ID", "Abstract", "CreatedOn"::timestamp, "Edition", "Title", "Year"
FROM old."Reference"
WHERE old."Reference"."ReferenceType" = 'Book'
    AND old."Reference"."Year" IS NOT NULL
    AND old."Reference"."Title" IS NOT NULL
ON CONFLICT (citavi_id) DO UPDATE 
    SET (abstract, createdon, edition, title, year) = (excluded.abstract, excluded.createdon, excluded.edition, excluded.title, excluded.year)
; 

错误代码:

    INSERT INTO new.bookmonographs  (citavi_id, abstract, createdon, edition, title, year)
SELECT "ID", "Abstract", "CreatedOn"::timestamp, "Edition", "Title", "Year"
FROM old."Reference", old."ReferenceAuthor"
WHERE old."Reference"."ReferenceType" = 'Book'
    AND old."Reference"."Year" IS NOT NULL
    AND old."Reference"."Title" IS NOT NULL
    AND old."ReferenceAuthor"."ReferenceID" = old."Reference"."ID"
    --Year, Title and Author must be present in the data, otherwise the entry is deemed useless, hence won't be included
ON CONFLICT (citavi_id) DO UPDATE 
    SET (abstract, createdon, edition, title, year) = (excluded.abstract, excluded.createdon, excluded.edition, excluded.title, excluded.year)
; 

我在 FROM 语句和另一个 WHERE 语句中添加了一个额外的源,以确保只有具有标题、年份和作者的条目被插入到新数据库中。 (如果 old."Reference"."ID" 存在于 old."ReferenceAuthor" 为 "ReferenceID",则存在作者。)即使没有附加的 WHERE 语句,查询也是错误的。我在 SELECT 中指定的列仅存在于 old."Reference" 中,而不存在于 old."ReferenceAuthor" 中。 目前old."ReferenceAuthor"old."Reference" 没有唯一约束,专着的唯一约束是:

CONSTRAINT bookmonographs_pk PRIMARY KEY (bookmonographsid),
CONSTRAINT bookmonographs_bookseries FOREIGN KEY (bookseriesid)
      REFERENCES new.bookseries (bookseriesid) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT bookmonographs_citaviid_unique UNIQUE (citavi_id)

PSQL 抛出的错误:

错误:ON CONFLICT DO UPDATE 命令不能再次影响行 提示:确保在同一命令中建议插入的行没有重复的约束值。 ********** 错误**********

错误:ON CONFLICT DO UPDATE 命令不能再次影响行 SQL 状态:21000 提示:确保在同一命令中建议插入的行没有重复的约束值。

我不知道出了什么问题,也不知道为什么提示指向重复的约束值。

【问题讨论】:

    标签: postgresql upsert


    【解决方案1】:

    postgres' docsctrl + f"Cardinality violation" errors in detail,因为没有直接链接)对此问题有很好的解释。

    引用文档:

    引发“基数违规”错误的想法是确保任何一行受到影响的每条语句不超过一次。在 SQL 标准讨论 SQL MERGE 的词典中,SQL 语句是“确定性的”。用户应该确信一行不会受到多次影响 - 如果不是这种情况,则无法预测多次受影响的行的最终值是什么。

    为了重放他们更简单的示例,在表 upsert 上,以下查询无法正常工作,因为我们无法可靠地知道 select val from upsert where key = 1 是否等于 'Foo' 或 'Bar':

     INSERT INTO upsert(key, val)
       VALUES(1, 'Foo'), (1, 'Bar')
       ON CONFLICT (key) UPDATE SET val = EXCLUDED.val;
     ERROR:  21000: ON CONFLICT UPDATE command could not lock/update self-inserted tuple
     HINT:  Ensure that no rows proposed for insertion within the same command have duplicate constrained values.
    

    【讨论】:

      【解决方案2】:

      问题是由于某些条目显然有多个作者这一事实引起的。因此,您编写的选择查询中的内部联接将为同一条目返回多行,INSERT ... ON CONFLICT 不喜欢这样。由于您只使用ReferenceAuthor 表进行过滤,因此您可以简单地重写查询,以便它使用该表仅通过对相关子查询执行exists 来过滤没有任何作者的条目。方法如下:

      INSERT INTO new.bookmonographs  (citavi_id, abstract, createdon, edition, title, year)
      SELECT "ID", "Abstract", "CreatedOn"::timestamp, "Edition", "Title", "Year"
      FROM old."Reference"
      WHERE old."Reference"."ReferenceType" = 'Book'
          AND old."Reference"."Year" IS NOT NULL
          AND old."Reference"."Title" IS NOT NULL
          AND exists(SELECT FROM old."ReferenceAuthor" WHERE old."ReferenceAuthor"."ReferenceID" = old."Reference"."ID")
          --Year, Title and Author must be present in the data, otherwise the entry is deemed useless, hence won't be included
      ON CONFLICT (citavi_id) DO UPDATE 
          SET (abstract, createdon, edition, title, year) = (excluded.abstract, excluded.createdon, excluded.edition, excluded.title, excluded.year)
      ; 
      

      【讨论】:

      • 注意:在正常情况更新(最终结果取决于源元组的随机排序)中,“每个目标的多个更新”会被默默忽略。在 ON CONFLICT 的情况下,每个目标元组的多个源元组要么导致递归/级联 CONFLICTS(同样:随机顺序问题),要么失败(如在 OQ 中)。
      • 像魅力一样工作,非常感谢。找出错误原因的思考过程是什么?还是遇到过这种问题?
      • @Telefonmann 我已经知道这个限制,但是错误信息也很清楚。
      【解决方案3】:

      使用显式INNER JOIN 将两个源表连接在一起:

      INSERT INTO new.bookmonographs  (citavi_id, abstract, createdon, edition, title, year)
      SELECT "ID", "Abstract", "CreatedOn"::timestamp, "Edition", "Title", "Year"
      FROM old."Reference"
      INNER JOIN old."ReferenceAuthor"                                       -- explicit join
          ON old."ReferenceAuthor"."ReferenceID" = old."Reference"."ID"      -- ON condition
      WHERE old."Reference"."ReferenceType" = 'Book' AND
            old."Reference"."Year" IS NOT NULL       AND
            old."Reference"."Title" IS NOT NULL
      ON CONFLICT (citavi_id) DO UPDATE 
      SET (abstract, createdon, edition, title, year) =
          (excluded.abstract, excluded.createdon, excluded.edition, excluded.title,
           excluded.year)
      

      【讨论】:

      • 不幸的是这不起作用,PSQL 会抛出同样的错误。 redneb 的解决方案虽然有效。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-02-04
      • 2018-08-02
      • 2016-05-11
      • 2016-07-21
      • 1970-01-01
      • 2022-06-15
      • 2021-02-16
      相关资源
      最近更新 更多