基于角色的数据库设计。作为用户属性或角色特定表的角色
Posted
技术标签:
【中文标题】基于角色的数据库设计。作为用户属性或角色特定表的角色【英文标题】:Role-based DB design. Role as user attribute or role-specific tables 【发布时间】:2020-10-24 23:14:17 【问题描述】:我正在为其用户开发一个具有多个角色的 Web 应用程序。使用的数据库是 Postgres 10。角色非常不同,并且有很多不重叠的数据,因此通常一个特定的表应该只属于一个角色而不属于另一个角色。
在下面的示例中,我将使用两个角色:seller 和 buyer。第一个角色分配给拥有一些公司并销售商品的用户。第二个角色分配给购买商品并可以收到礼物的用户。 卖家不能接受礼物,买家不能有公司。 seller 和 buyer 都可以有头像。
我正在考虑以下四种设计及其优缺点。
案例一
这里我们有一个 role reference table 枚举系统中的所有角色(seller 或 buyer),user 表,其中包含引用角色表的用户的密码和电子邮件,avatar、company 和 gifts_recieved 表引用 user 表。
专业人士
没有表具有可空属性缺点
应该使用触发器来检查参照完整性。例如,如果 id=1 的 user 是 buyer,我们应该阻止向 company 插入一行em> 表,user_id=1。因此,我们应该在 每个 特定于角色的表上创建一个触发器,该触发器引用 user 表以保护参照完整性。案例 2
这里,user 表包含可为空的角色属性:seller_id 和 buyer_id(或示例中没有的其他角色)。其中只有一个不是 NULL,它定义了用户的角色。所有特定于角色的表都引用 seller 或 buyer 表。两个角色通用的 Avatar 表继续引用 user 表。
专业人士
不需要触发器来保护角色特定表的引用完整性:它们都引用 seller 或 buyer 表。缺点
可以为 NULL 的角色属性。 需要一个触发器(不是在情况 1 中触发所有表)来确保只有一个角色属性(seller_id 或 buyer_id)不为 NULL 并且所有其他为 NULL。案例 3
为了避免 NULL-able 角色属性,我们将使用 Postgres 的 inheritance 用于表(mysql 不支持此功能)。在这里,user 表被“吞噬”到 seller 和 buyer 表中。 Avatar 表已重复并以角色名称为前缀。
专业人士
没有可以为 NULL 的角色属性。 根本没有触发器。缺点
不同角色通用的表被重复 - 纯粹是资源浪费。 继承是 Postgres 特有的功能。案例4
为了解决案例 3 中的重复问题,将 avatar_id 移动到 seller 和 buyer的 user 表> 继承。因此,对于 seller 和 buyer 共有的每个表,我们将为它们的父表 (user) 添加一个属性。
专业人士
没有参照完整性的触发器 没有表重复 没有可以为 NULL 的属性缺点
Postgres 继承有其注意事项:https://www.postgresql.org/docs/10/ddl-inherit.html问题
上述哪种解决方案适合最佳数据库设计实践(如果有)?用户角色应该是用户表的属性还是特定的表?
澄清
-
一个卖家只能拥有一家公司。
通过电子邮件/密码对标识的用户可以是卖家也可以是买家。
【问题讨论】:
不清楚是只有一家公司还是几家公司。此外,尚不清楚卖方是否也可以成为客户,如果有多个公司,则公司中的卖方可能是另一家公司的客户。在那种情况下,我会选择案例 1,但没有触发器,并让 Web 应用程序的服务器端做出在案例 1 中使用触发器的决定。无论如何,如果我需要更改数据库提供程序,我会避免继承。设计实践虽然很棘手,但我经常不得不在特定情况下发明自己的实践。 卖家只有一家公司,用户可以是卖家也可以是买家。我已对原始问题进行了说明。 【参考方案1】:我偏爱案例 2。
由于使用了继承,我不会考虑案例 3 和 4。我已经很多年没有讨论过这个话题了,但我最关心的问题是使用数据库实现继承的开发人员是否能够思考如何使用它。
在我看来,触发点是案例 1 的症结所在。
您是否知道在案例 2 中不需要触发器?
create table exclusion_constraint (
user_id int,
seller_id int,
buyer_id int,
check (case
when coalesce(seller_id, buyer_id) is null then false
when seller_id is not null and buyer_id is not null then false
else true
end)
);
CREATE TABLE
insert into exclusion_constraint values (1, 100, 200);
ERROR: new row for relation "exclusion_constraint" violates check constraint "exclusion_constraint_check"
DETAIL: Failing row contains (1, 100, 200).
insert into exclusion_constraint values (2, null, 200);
INSERT 0 1
insert into exclusion_constraint values (3, 100, null);
INSERT 0 1
insert into exclusion_constraint values (4, null, null);
ERROR: new row for relation "exclusion_constraint" violates check constraint "exclusion_constraint_check"
DETAIL: Failing row contains (4, null, null).
【讨论】:
感谢您的提示,非常好的检查约束!以上是关于基于角色的数据库设计。作为用户属性或角色特定表的角色的主要内容,如果未能解决你的问题,请参考以下文章