我有一个类似于此的模式:

create table users (

  id bigint primary key generated always as identity,
  name text not null

);

create table feature (

  id bigint primary key generated always as identity,
  value text not null check (value != '')

);

create unique index ux1 on feature(value);

-- link table
create table user_feature (

  id bigint generated always as identity,
  user_id bigint not null references users(id),
  feature_id bigint not null references feature(id)
);

create unique index ux2 on user_feature(user_id, feature_id);

-- feature values
insert into feature (value) values ('A'), ('B'), ('C'), ('D');

因此,用户可以有0个或多个功能,他可以只有一种类型的相同功能,但我想在用户可以拥有的功能之间添加一些逻辑/条件.

例如,假设功能BC是不兼容的,因此用户可以没有它们,BC,但不能两者都有,不能将BC放在一起.

如果有的话,有什么适当的方法来做到这一点?

我正在阅读,如果我没有错,唯一的选项是触发器对吗(在插入/更新时)? 我不习惯触发器,如果有人能至少举个例子的话.

我使用的是版本14.

谢谢你的帮助.

推荐答案

我会避免触发点.如果两个用户在单独的事务中插入值B和C,您的触发器将不会捕获它,因 for each 会话将只看到它插入的值.

您可以将唯一索引与CASE表达式一起使用:

create unique index u3 on user_feature(user_id, 
                                       (case when feature_id in (2 /* B */, 3 /* C */) then 1 
                                             else null 
                                        end));

这个唯一的索引

  • 忽略除B或C之外的任何特征(假设B具有ID 2,C具有ID 3)
  • 防止为同一用户插入B和C(因为它们被映射到CASE表达式中的相同值

然而,这种方法在一定程度上受到了限制:

  • 您的要素需要遵循等价关系,即如果不允许使用B和C,不允许使用C和D,则也必须禁止使用B和D
  • 在创建约束时,您的主键必须是已知的(在这种情况下,我会放弃generated as identity并显式提供PK值)

UPDATE正如@res1在他们的 comments 中指出的:第一个限制并不是真正的问题,因为您可以为您想要禁止的每个组合添加一个单独的唯一索引.

SQL Fiddle

Sql相关问答推荐

创建每小时重置的序列号

如何根据SQL中的列条件获取下一个时间戳?

如何转换和汇总行数

为什么Prisma生成唯一索引,而不是基于方案上的唯一列约束?

了解放置时的连接

SQL子查询返回多个值错误

查询页面推荐

为什么我的SQL标量函数有时会抛出";子查询返回多个值.这是不允许的.

SQL按日期分组字段和如果日期匹配则求和

在PostgreSQL中汇总连接表中的 case 值

SQL中相同表内的VLOOKUP等价

从选定记录中提取摘要作为值的划分

使用 XML 作为 SQL 表

获取多个开始-结束时间戳集之间经过的时间

根据开始时间和结束时间计算has_impact字段

使用in和and运算符过滤记录的条件

强制 SQL 始终通过 R 从视图中返回至少一行

如何筛选 GROUP BY 结果? HAVING 没有产生预期的结果

如何对 SQL 表中的连续时间戳进行分组?

如何使用子查询锁定此查询中的选定行?