Postgres条件唯一约束

时间:2017-02-17 18:54:01

标签: postgresql unique-constraint postgresql-9.4 rails-postgresql unique-index

假设我有一个用户表,其中用户是特定租户的成员,他们的电子邮件是唯一索引到他们的租户的,如下所示:

User
id  |  tenant_id  |  email
1      1             person1@example.com
2      1             person2@example.com

允许此用户,因为尽管有重复的电子邮件,但他们在不同的租户:

3      2             person1@example.com

此用户被阻止,因为该电子邮件在同一租户处是重复的:

4      2             person1@example.com <--- will throw an error

我们有很多独特的索引 - 这部分很容易。

现在假装我希望能够添加一个可以访问所有租户的全局用户,但前提是该表中根本不存在该电子邮件。此外,一旦记录存在,其他任何人 - 无论是否租用 - 都将能够使用相同的电子邮件。

为清楚起见,全局用户可能只有一个空租户ID,但我们可能还会添加一个global布尔值。

有没有办法为这个逻辑编写约束?您不能简单地使电子邮件全局受到限制,因为它们不能在租户之间重复,如果您使用空租户ID进行索引,如果有租用的用户具有相同的e,则postgres将允许未经授权的用户-mail。

我查看了排除约束和检查,但无法弄清楚如何将它们组合在一起(如果tenant_id为null,则全局唯一地约束电子邮件,并在插入任何内容时检查具有空租户ID和匹配电子邮件的记录记录)。

请不要问为什么我这样做 - 我的桌子实际上不是用户,我们已经考虑并解雇了其他架构:)

提前致谢!

2 个答案:

答案 0 :(得分:1)

根据PostgreSQL文档,您可以创建unique partial index,这与在表上创建唯一的部分约束实际上相同:

NSNumbers

使用此技术,您可以为管理员和非管理员用户创建2个单独的唯一索引。

答案 1 :(得分:0)

您可以对两个字段使用UNIQUE约束:

create table myUsers
(
    id int not null,
    tenant int not null,
    email varchar(200) not null,
    UNIQUE(email, tenant)
);

insert into myUsers values
(1, 1, 'person1@example.com'),
(2, 1, 'person2@example.com');

insert into myUsers values
(3, 2, 'person1@example.com');

下一次插入会抛出错误:

insert into myUsers values
(4, 2, 'person1@example.com');
  

错误,警告:

     

23505:重复键值违反了唯一约束   &#34; myusers_email_tenant_key&#34;

在此处查看:http://rextester.com/AJZVI34616

问题的第二部分:

  

现在假装我希望能够添加一个可以访问所有租户的全局用户,但前提是该电子邮件根本不存在于表中。

一种解决方案可能是为管理员用户预留租户:

tenant = 0  <-- admin users

但UNIQUE约束允许重复的电子邮件,我建议您在此表中添加rol字段,或者为此目的使用另一个管理员用户表。

就我而言,我们使用两个表,并且都有一个rol字段。