简体   繁体   English

冲突部分索引的 Postgres 唯一或排除约束无法更新票证

[英]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 不更新现有票证?

Setup设置

  1. Tickets table:门票表:
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);
  1. 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只能是以下值之一: openunreadcompleted
  2. 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";

Problem问题

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).我想要的只是更新“未完成”票证的statuslast_updated_atlast_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 子句中按名称引用约束。

from the documentation从文档

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM