[英]Postgres unique or exclusion constraint for partial index on conflict fails to update tickets
PostgreSQL database question for a typical ticketing system.典型票务系统的 PostgreSQL 数据库问题。 Why my upsert does not update an existing ticket?
为什么我的 upsert 不更新现有票证?
CREATE TABLE ticket (
ticket_id SERIAL PRIMARY KEY,
user_id uuid NOT NULL
coach_id uuid,
status text NOT NULL,
last_message text NOT NULL,
last_updated_at timestamp with time zone NOT NULL,
completed_at timestamp with time zone
);
-- Indices -------------------------------------------------------
CREATE UNIQUE INDEX ticket_ak1 ON ticket(ticket_id int4_ops);
CREATE UNIQUE INDEX ticket_user_id_not_completed_idx ON ticket(user_id uuid_ops,(status <> 'completed'::text) bool_ops DESC NULLS LAST);
Constraints developed in code (not part of db's enum type):在代码中开发的约束(不是 db 的枚举类型的一部分):
status
can only be one of the following values: open
, unread
or completed
. status
只能是以下值之一: open
、 unread
或completed
。 Tickets available before:之前可用的门票:
INSERT INTO "ticket" ("user_id","coach_id","status","last_message","last_updated_at","ticket_id") VALUES ('d5948d24-6fce-4712-896a-e15cd6db6837',NULL,'open','Accusantium perferendis voluptatem sit aut consequatur.','2021-12-13 17:24:48.389',1) RETURNING "ticket_id";
INSERT INTO "ticket" ("user_id","coach_id","status","last_message","last_updated_at","completed_at","ticket_id") VALUES ('d5948d24-6fce-4712-896a-e15cd6db6837',NULL,'completed','Aut consequatur perferendis sit accusantium voluptatem.','2021-12-13 17:24:48.391','2021-12-13 17:24:48.391',2) RETURNING "ticket_id";
Running this SQL to try and upsert the first ticket
fails:运行此 SQL 以尝试插入第一
ticket
失败:
INSERT INTO "ticket" ("user_id","coach_id","status","last_message","last_updated_at") VALUES ('ab45ae3f-e84a-4a0a-8072-8896a902d488',NULL,'unread','You are tearing me apart, Brandon!','2021-12-13 17:24:48.389')
ON CONFLICT ("user_id") DO UPDATE SET "status"="excluded"."status",
"last_updated_at"="excluded"."last_updated_at",
"last_message"="excluded"."last_message"
WHERE "excluded"."status" <> 'completed' RETURNING "ticket_id"
with error message:带有错误消息:
ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification
错误:没有与 ON CONFLICT 规范匹配的唯一或排除约束
I've tried changing it to:我尝试将其更改为:
INSERT INTO "ticket" ("user_id","coach_id","status","last_message","last_updated_at") VALUES ('ab45ae3f-e84a-4a0a-8072-8896a902d488',NULL,'unread','You are tearing me apart, Brandon!','2021-12-13 17:24:48.389')
ON CONFLICT (user_id) WHERE status <> 'completed' DO UPDATE
SET "status"="excluded"."status",
"last_updated_at"="excluded"."last_updated_at",
"last_message"="excluded"."last_message"
moving the WHERE
clause before DO UPDATE
to trigger the partial index query, but to no avail.在
DO UPDATE
之前移动WHERE
子句以触发部分索引查询,但无济于事。
All I want is to update status
, last_updated_at
and last_message
of a "non-completed" ticket (which should be only one per user as per partial unique index defined on that table).我想要的只是更新“未完成”票证的
status
、 last_updated_at
和last_message
(根据该表上定义的部分唯一索引,每个用户应该只有一个)。 So, again, why this upsert does not update an existing ticket?那么,为什么这个 upsert 不更新现有票证呢?
There is error message is the give away.有错误信息是赠品。 Your on conflict constraint does not match any unique constraints declared against the table.
您的冲突约束与针对表声明的任何唯一约束不匹配。 ?
?
The column user_id exists along with other columns in your index ticket_user_id_not_completed_idx
so there is no exact match.列 user_id 与索引
ticket_user_id_not_completed_idx
中的其他列一起存在,因此没有完全匹配。 Your on conflict needs to match your index exactly.您的冲突需要与您的索引完全匹配。 Either change your unique index to be just the
user_id
column or add all the columns to your on conflict clause.要么将您的唯一索引更改为只是
user_id
列,要么将所有列添加到您的 on conflict 子句中。
Or或者
You can reference the constraint by name in the on conflict clause.您可以在 on conflict 子句中按名称引用约束。
So, to have a ticketing system with a single not-completed ticket per user, all I had to do was to fix the index, by specifying both columns as part of that index, even if there's a where clause to define partiality of that index:因此,要拥有一个票务系统,每个用户只有一张未完成的票,我所要做的就是修复索引,通过将两列指定为该索引的一部分,即使有一个 where 子句来定义该索引的偏倚:
CREATE UNIQUE INDEX ticket_user_id_not_completed_idx
ON ticket(user_id, status) WHERE status <> 'completed'
instead of:代替:
CREATE UNIQUE INDEX ticket_user_id_not_completed_idx
ON ticket(user_id uuid_ops,(status <> 'completed'::text) bool_ops DESC NULLS LAST);
That's also what VynlJunkie said in the previous comment.这也是 VynlJunkie 在之前的评论中所说的。 I just wanted to have this on record how I solved it in the end.
我只是想记录下我最终如何解决它。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.