sql - Postgres upsert 使用冲突 - 冲突条件下的多个字段
问题描述
我正在尝试在postgres
数据库中实现喜欢/不喜欢某个项目的概念——当用户喜欢/不喜欢某样东西时,我想在我的数据库中插入一个项目来表示这一点。
这是我的架构:
id | postID | userID | type
1 2 1 like
现在,如果用户已经喜欢该项目,现在他们决定不喜欢它 - 我想更新该type
字段,从like
到dislike
。
同样,如果他们不喜欢某些东西,现在决定喜欢它,我想执行相反的更新。
此外,用户只能喜欢/不喜欢某件事 - 因此,如果用户之前喜欢/不喜欢该帖子,现在决定再次喜欢/不喜欢它,则不会发生任何事情。
这意味着我需要在 中实现一个 upsert 语句postgres
,如果用户之前没有与帖子交互,则插入一个新行,如果指定的行已经存在,则更新该type
字段。postID + userID + type
我正在考虑使用on conflict
语法来执行此操作 -
INSERT INTO table_name(postID,userID,type)
VALUES(2,1,'like')
ON CONFLICT (????) DO UPDATE
SET type = 'like'
但我不确定将什么传递到该ON CONFLICT
部分,因为匹配需要在多个字段上进行。
我考虑在 (postID, userID) 字段上设置唯一索引 - 如下所示:
create unique index idx_1 on table (postID, userID)
问题是我将来想使用这个数据库来存储评论信息,并且允许用户多次评论同一个帖子。
一个例子是:
id | postID | userID | type
1 2 1 comment
2 2 1 comment
3 2 1 like
解决方案
如果要限制对一行的更新次数,可以使用check
约束和辅助列来计算更新。
alter table t add column num_updates int default 1 check (num_updates <= 2);
然后,如果您想防止 以外的类型出现重复行comment
,您可以使用过滤的唯一索引:
在 table_name(postid, userid) 上创建唯一索引 unq_table_name_postid_userid,其中 type <> 'comment';
然后我认为您可以使用以下方式表达逻辑on conflict
:
INSERT INTO table_name (postID, userID, type)
VALUES(2, 1, 'like')
ON CONFLICT (postID, userID) DO UPDATE
SET type = 'like',
num_updates = excluded.num_updates + 1;
这仅允许一次更新。您可能需要更精细的逻辑,例如仅在type
更改时更新值:
num_update = (excluded.num_updates = num_updates)::int + 1