考虑以下表格:
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是一个位标志,指定可以读取新闻项的某些角色组合。因此,如果我插入一个可以通过限制和保密方式阅读的新闻项目,我会将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作为我的应用程序。
UPDATE 我对数据库没有使用索引进行位操作,这需要进行全表扫描才能获得性能。所以我更新了问题以更紧密地反映我的情况,数据库中的每一行都属于一个特定的公司,因此所有查询都将包含WHERE子句,其中包含一个将在其上有索引的company_fk。
更新我现在只有6个角色,将来可能更多。
UPDATE 角色不是互斥的,并且它们彼此继承,例如,restricted会继承分配给public的所有权限。
答案 0 :(得分:9)
缺点:难以写入数据,难以读取数据,难以调试,尤其是:查询速度慢,因为数据库无法在此类查询上使用索引。
优点,您节省了几个字节。与BIT字段相比,您可以在一百万个记录表上节省几MB ...几乎不值得。 :)
答案 1 :(得分:8)
如果您只有少数几个角色,则甚至不会在 PostgreSQL 中保存任何存储空间。 integer
列使用4个字节,bigint
个8字节。两者都可能需要对齐填充:
boolean
列使用1个字节。实际上,您可以为一个integer
列添加四个或更多布尔列,为bigint
添加八个或更多。
另请注意,NULL
值仅在simplified中使用一位(NULL bitmap)。
单个列更易于阅读,索引。其他人已经对此发表了评论。
您仍然可以利用indexes on expressions或partial indexes来规避索引问题(“不可攻击”)。广义陈述如:
数据库不能在像这样的查询上使用索引
或
这些条件是非SARG的!
不完全正确 - 也许对于其他一些缺乏这些功能的RDBMS 但是,为什么要避免完全避免这个问题呢?
正如你已经澄清的那样,我们谈论的是6种不同的类型(可能更多)。使用单个boolean
列。与bigint
相比,您甚至可以节省空间。在这种情况下,空间要求似乎并不重要。
如果这些标志互斥,您可以使用一个<{3}}类型的列或一个小型查找表和引用它的外键。 (排除了问题更新。)
答案 2 :(得分:6)
这里至少有一个巨大的劣势......
这些条件不具备SARG!
这是一个很大的问题,对我来说这将是一个破坏者。您需要执行的按位评估(据我所知)在任何数据库中都不可索引 - 引擎需要检查每一行以执行评估,这意味着可怕的性能。
答案 3 :(得分:0)
除了为SQL Server的实现提供先前的答案之外,通过拥有单个位域整数和一堆BIT NOT NULL
列,您将不会节省任何空间:
SQL Server数据库引擎优化了 bit 列的存储。如果表中有8个或更少的 bit 列,则这些列将存储为1个字节。如果存在9至16个 bit 列,则这些列将存储为2个字节,依此类推。
如JNK所述,对位域整数的部分比较将无法保存,因此,除非立即比较整个值,否则对位域整数的索引将毫无用处。
SQL Server上的磁盘索引是基于排序的,因此要单独设置具有特定位的行,将需要为每个位列使用单独的索引。如果只查找1,则节省空间的一种方法是使它们成为仅存储1值的过滤列(零值根本没有索引条目)。
CREATE TABLE news(
pkey INT IDENTITY PRIMARY KEY,
title VARCHAR(256),
company_fk INTEGER REFERENCES compaines(pkey), -- updated since asking the question
body VARCHAR(512),
public_role BIT NOT NULL DEFAULT 0,
restricted_role BIT NOT NULL DEFAULT 0,
confidential_role BIT NOT NULL DEFAULT 0,
secret_role BIT NOT NULL DEFAULT 0
);
CREATE UNIQUE INDEX ByPublicRole ON news(public_role, pkey) WHERE public_role=1;
CREATE UNIQUE INDEX ByRestrictedRole ON news(restricted_role, pkey) WHERE restricted_role=1;
CREATE UNIQUE INDEX ByConfidentialRole ON news(confidential_role, pkey) WHERE confidential_role=1;
CREATE UNIQUE INDEX BySecretRole ON news(secret_role, pkey) WHERE secret_role=1;
select * from news WHERE company_fk=2 AND restricted_role=1 OR confidential_role=1;
select * from news WHERE company_fk=2 AND restricted_role=1 AND confidential_role=1;
一如既往,索引应基于实际查询使用情况,并与维护成本保持平衡。