考虑下表:
CREATE TABLE user_roles(
pkey SERIAL PRIMARY KEY,
bit_id BIGINT NOT NULL,
name VARCHAR(256) NOT NULL,
);
INSERT INTO user_roles (bit_id,name) VALUES (1,'public');
INSERT INTO user_roles (bit_id,name) VALUES (2,'restricted');
INSERT INTO user_roles (bit_id,name) VALUES (4,'confidential');
INSERT INTO user_roles (bit_id,name) VALUES (8,'secret');
CREATE TABLE news(
pkey SERIAL PRIMARY KEY,
title VARCHAR(256),
company_fk INTEGER REFERENCES compaines(pkey), -- updated since asking the question
body VARCHAR(512),
read_roles BIGINT -- bit flag
);
read_roles 是一个位标志,指定可以读取新闻项的一些角色组合。因此,如果我要插入一个可以由受限和 secret 阅读的新闻项目,我会将 read_roles 的值设置为 2 | 4
或 6,当我想取回特定用户可以看到的新闻帖子时,我可以使用类似的查询。
select * from news WHERE company_fk=2 AND (read_roles | 2 != 0) OR (read_roles | 4 != 0) ;
select * from news WHERE company_fk=2 AND read_roles = 6;
一般来说,在数据库列中使用位标志有什么缺点?我假设这个问题的答案可能是特定于数据库的,所以我有兴趣了解特定数据库的缺点。
我正在为我的应用程序使用 Postgres 9.1。
更新 我了解到数据库不使用索引进行位操作,这需要全表扫描,这会降低性能。因此,我更新了问题以更准确地反射(reflect)我的情况,数据库中的每一行都属于一个特定的公司,因此所有查询都将包含一个包含 company_fk 的 WHERE 子句,该子句上将有一个索引。
更新 我现在只有 6 个角色,将来可能会更多。
UPDATE 角色不是互斥的,它们相互继承,例如,restricted 继承分配给 public 的所有权限。
最佳答案
如果您只有少数几个角色,您甚至不会在 PostgreSQL 中保存任何存储 空间。一个integer
列使用 4 个字节,一个 bigint
8 个字节。两者都可能需要对齐填充:
A boolean
列使用 1 个字节。实际上,您可以为一个 integer
列放置四个或更多 bool 列,为 bigint
放置八个或更多列。
还要考虑到 NULL
值仅在 simplified 中使用一位 ( NULL bitmap ) .
单独的列更易于阅读和索引。其他人已经对此发表了评论。
您仍然可以使用 indexes on expressions或 partial indexes规避索引问题(“不可搜索”)。概括性陈述如:
database cannot use indexes on a query like this
或
These conditions are non-SARGable!
不完全正确 - 也许对于其他缺少这些功能的 RDBMS。
但是,既然可以完全避免问题,为什么还要规避呢?
正如您所阐明的,我们正在谈论 6 种不同的类型(可能更多)。使用单独的 boolean
列。与一个 bigint
相比,您甚至可能会节省空间。在这种情况下,空间要求似乎并不重要。
如果这些标志互斥,您可以使用一个 enum
类型的列或者一个小的查找表和一个引用它的外键。 (排除问题更新。)
关于sql - 数据库列中的位标志有什么缺点吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12270461/