假设我有一个项目表:
CREATE TABLE items
(
item serial PRIMARY KEY,
...
);
现在我想介绍每个项目的“权限”概念(请注意,我并不是在谈论数据库访问权限,而是该项目的业务逻辑权限)。每个项目都有默认权限和每个用户的权限,这可能会覆盖默认权限。
我尝试思考如何实现这个功能,并想出了以下解决方案:
1)布尔解决方案
为每个权限使用一个布尔列:CREATE TABLE items
(
item serial PRIMARY KEY,
can_change_description boolean NOT NULL,
can_change_price boolean NOT NULL,
can_delete_item_from_store boolean NOT NULL,
...
);
CREATE TABLE item_per_user_permissions
(
item int NOT NULL REFERENCES items(item),
user int NOT NULL REFERENCES users(user),
PRIMARY KEY(item, user),
can_change_description boolean NOT NULL,
can_change_price boolean NOT NULL,
can_delete_item_from_store boolean NOT NULL,
...
);
: 每个权限都有名称。
: 有数十个权限,这会显著增加列数,并且您必须在两个表中分别定义它们。
2) 整数解决方案
使用整数并将其视为位字段(即,位0用于can_change_description
,位1用于can_change_price
等,并使用位运算设置或读取权限)。
CREATE DOMAIN permissions AS integer;
优点: 非常快。
缺点: 您必须在数据库和前端界面中跟踪哪个位表示哪个权限。
3) 位字段解决方案
与2)相同,但使用bit(n)
。可能具有相同的优点和缺点,可能略慢。
4) 枚举解决方案
使用枚举类型来表示权限:
CREATE TYPE permission AS ENUM ('can_change_description', 'can_change_price', .....);
然后为默认权限创建一个额外的表:
CREATE TABLE item_default_permissions
(
item int NOT NULL REFERENCES items(item),
perm permission NOT NULL,
PRIMARY KEY(item, perm)
);
并将每个用户的定义表更改为:
CREATE TABLE item_per_user_permissions
(
item int NOT NULL REFERENCES items(item),
user int NOT NULL REFERENCES users(user),
perm permission NOT NULL,
PRIMARY KEY(item, user, perm)
);
优点: 方便命名单个权限(无需处理位位置)。
缺点: 即使只是检索默认权限,也需要访问两个额外的表:首先是默认权限表,其次是存储枚举值的系统目录。
特别是因为必须为每个项的每个页面视图检索默认权限,最后一种选择可能会对性能产生显着影响。
您能想到其他选择吗?
应采取哪种方法?
请注意:此问题已在DBA上重新发布。
bit(n)
并提供良好的文档。使用bigint
也不适合具有如此多权限位,因为您只有 64 位可供使用。顺便说一句,我同意枚举数组很丑陋,但您正在尝试进行一种优化,牺牲了性能以换取规范化,否则您将只拥有一个子表。所以您已经忽略了“干净”的方式(在我看来是明智的)。 - Craig Ringer主键(object_type,bit_position)
。你可以使用枚举,但是没有太多好处,而且枚举具有浮点内部值,以允许非破坏性中间值插入,因此它们并不是真正理想的选择。然后,有一个代码生成器,从你的数据库权限表中创建一个类。或者从代码中创建权限表的SQL。或者从版本控制中间表示创建两者。无论如何。 - Craig Ringer