我在 PostgreSQL 11 数据库中有以下(简化的)表
examples
:
| Column | Type
| id | uuid
| core_id | character varying(255)
| name | character varying(255)
Indexes:
"examples_core_id_pkey" PRIMARY KEY, btree (core_id)
"examples_core_id_key" UNIQUE CONSTRAINT, btree (core_id)
"examples_id_unique" UNIQUE CONSTRAINT, btree (id)
现在,让我们考虑以下 SQL 语句:
INSERT INTO examples
( id, core_id, name)
VALUES
( $id, $coreId, $name)
ON CONFLICT (core_id)
DO UPDATE
SET
name = $name
当我尝试两次插入时
我有时会收到
SequelizeUniqueConstraint
错误:
重复的键值违反了唯一约束
examples_id_unique
我不明白为什么会发生这种情况,因为我在
core_id
子句中指定了 ON CONFLICT
。因此,我对其进行解释,以便如果 core_id 缺乏唯一性,只需执行 UPDATE
并且不要触摸 id
。我认为 postgres 在这些情况下只会检查仲裁者索引 (core_id) 并运行更新。
可选的 ON CONFLICT 子句指定了替代操作 引发唯一违规或排除约束违规错误。 对于建议插入的每个单独行,插入 继续,或者,如果仲裁器约束或索引指定 违反了conflict_target,则采取替代conflict_action。
我想知道这是否是一个并发问题,因为在生产环境中,我得到以下流程:
这似乎与这个问题非常相似具有唯一约束的INSERT ON CONFLICT DO UPDATE SET(UPSERT)语句在并发运行时会生成约束违规但尚未找到此问题的解决方案。
UPSERT 选择的“仲裁者索引”由
ON CONFLICT
子句中声明的“冲突目标”确定。 说明书:
可以执行独特的索引推断。进行推理时,它由一个或多个组成conflict_target
列和/或index_column_name
表达式,以及可选的index_expression
。全部index_predicate
唯一索引,无论顺序如何,都完全包含table_name
指定的列/表达式 被推断(选择)作为仲裁索引。conflict_target
在您的情况下,
ON CONFLICT (id, core_id)
(仅!)确定多列唯一约束id_core_id_unique
。但不是其他三个唯一约束/索引。仅针对所选仲裁器索引抑制唯一违规。对于多个不同的约束,确定替代行动将是不明确的。
您实际上有 4 唯一索引:
"examples_id_pkey" PRIMARY KEY, btree (core_id)
"examples_core_id_key" UNIQUE CONSTRAINT, btree (core_id)
"examples_id_unique" UNIQUE CONSTRAINT, btree (id)
"id_core_id_unique" UNIQUE CONSTRAINT, btree (core_id, id)
约束(和索引)
examples_core_id_key
是 100% 冗余,因为 PK 索引已经使用相同的唯一索引实现。在您方便的时候尽早删除该约束(也隐式地删除索引)。
剩下两个唯一的索引不能容忍重复的输入。从多个不同的
UNIQUE
(和 EXCLUSION
)约束捕获唯一违规的唯一方法是通用子句 ON CONFLICT DO NOTHING
。参见:
或者你摆脱其他重叠的约束。您真的需要全部(剩余的)三个吗?对于
UNIQUE
上有 (core_id, id)
的表来说,PRIMARY KEY
上的 (core_id)
约束没有什么意义。对于半标准化设计来说,多个 UNIQUE
约束是罕见的例外......