数据库列中位标志的任何缺点?

时间:2012-09-04 19:54:31

标签: sql sql-server oracle postgresql database-design

考虑以下表格:

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的所有权限。

4 个答案:

答案 0 :(得分:9)

缺点:难以写入数据,难以读取数据,难以调试,尤其是:查询速度慢,因为数据库无法在此类查询上使用索引。

优点,您节省了几个字节。与BIT字段相比,您可以在一百万个记录表上节省几MB ...几乎不值得。 :)

答案 1 :(得分:8)

如果您只有少数几个角色,则甚至不会在 PostgreSQL 中保存任何存储空间。 integer列使用4个字节,bigint个8字节。两者都可能需要对齐填充:

boolean列使用1个字节。实际上,您可以为一个integer列添加四个或更多布尔列,为bigint添加八个或更多。

另请注意,NULL值仅在simplified中使用一位(NULL bitmap)。

单个列更易于阅读,索引。其他人已经对此发表了评论。

您仍然可以利用indexes on expressionspartial 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个字节,依此类推。

     

bit at docs.microsoft.com

如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;

这两个查询都使用我产生的随机测试数据制定了一个不错的计划: bit plans

一如既往,索引应基于实际查询使用情况,并与维护成本保持平衡。