冲突时部分索引的 Postgres 唯一或排除约束无法更新票证
Postgres unique or exclusion constraint for partial index on conflict fails to update tickets
PostgreSQL 典型票务系统的数据库问题。为什么我的 upsert 没有更新现有的工单?
设置
- 门票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);
在代码中开发的约束(不是数据库枚举类型的一部分):
status
只能是以下值之一:open
、unread
或 completed
.
售票时间:
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";
问题
运行 此 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"
错误信息:
ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification
我试过将其更改为:
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"
将WHERE
子句移到DO UPDATE
之前触发部分索引查询,但无济于事。
我只想更新“未完成”票证的 status
、last_updated_at
和 last_message
(根据定义的部分唯一索引,每个用户应该只有一张在那table)。那么,为什么这个 upsert 不更新现有的工单?
有错误信息是赠品。您的 on 冲突约束与针对 table 声明的任何唯一约束都不匹配。 ?
列 user_id 与索引 ticket_user_id_not_completed_idx
中的其他列一起存在,因此没有完全匹配。您的 on 冲突需要与您的索引完全匹配。要么将您的唯一索引更改为 user_id
列,要么将所有列添加到您的 on conflict 子句中。
或
您可以在 on 冲突子句中按名称引用约束。
因此,要让票务系统为每个用户提供一张未完成的票,我所要做的就是修复索引,通过将两列都指定为该索引的一部分,即使有一个 where 子句定义该指数的偏向性:
CREATE UNIQUE INDEX ticket_user_id_not_completed_idx
ON ticket(user_id, status) WHERE status <> 'completed'
而不是:
CREATE UNIQUE INDEX ticket_user_id_not_completed_idx
ON ticket(user_id uuid_ops,(status <> 'completed'::text) bool_ops DESC NULLS LAST);
VynlJunkie 在之前的评论中也是这么说的。我只是想记录一下我最后是如何解决的。
PostgreSQL 典型票务系统的数据库问题。为什么我的 upsert 没有更新现有的工单?
设置
- 门票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);
在代码中开发的约束(不是数据库枚举类型的一部分):
status
只能是以下值之一:open
、unread
或completed
.
售票时间:
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";
问题
运行 此 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"
错误信息:
ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification
我试过将其更改为:
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"
将WHERE
子句移到DO UPDATE
之前触发部分索引查询,但无济于事。
我只想更新“未完成”票证的 status
、last_updated_at
和 last_message
(根据定义的部分唯一索引,每个用户应该只有一张在那table)。那么,为什么这个 upsert 不更新现有的工单?
有错误信息是赠品。您的 on 冲突约束与针对 table 声明的任何唯一约束都不匹配。 ?
列 user_id 与索引 ticket_user_id_not_completed_idx
中的其他列一起存在,因此没有完全匹配。您的 on 冲突需要与您的索引完全匹配。要么将您的唯一索引更改为 user_id
列,要么将所有列添加到您的 on conflict 子句中。
或
您可以在 on 冲突子句中按名称引用约束。
因此,要让票务系统为每个用户提供一张未完成的票,我所要做的就是修复索引,通过将两列都指定为该索引的一部分,即使有一个 where 子句定义该指数的偏向性:
CREATE UNIQUE INDEX ticket_user_id_not_completed_idx
ON ticket(user_id, status) WHERE status <> 'completed'
而不是:
CREATE UNIQUE INDEX ticket_user_id_not_completed_idx
ON ticket(user_id uuid_ops,(status <> 'completed'::text) bool_ops DESC NULLS LAST);
VynlJunkie 在之前的评论中也是这么说的。我只是想记录一下我最后是如何解决的。