我正在使用PostgreSQL数据库中的一个表,其中包含多个布尔列来确定某些状态(例如已发布
,可见
等)。 我想创建一个单一状态列,以位掩码的形式存储所有这些值以及可能的新值。 在这种情况下,integer
和bit(n)
之间有什么区别吗?
由于该表存储用户通过Web界面创建的对象,因此它将是一个相当大的表。 因此,我认为我必须对此列使用(部分)索引。
我正在使用PostgreSQL数据库中的一个表,其中包含多个布尔列来确定某些状态(例如已发布
,可见
等)。 我想创建一个单一状态列,以位掩码的形式存储所有这些值以及可能的新值。 在这种情况下,integer
和bit(n)
之间有什么区别吗?
由于该表存储用户通过Web界面创建的对象,因此它将是一个相当大的表。 因此,我认为我必须对此列使用(部分)索引。
如果您只有几个变量,我建议保持单独的布尔
列。
布尔
列允许单独位使用NULL
值,如果需要的话。如果不需要可以始终定义列NOT NULL
。整数
列可能最适合。(或者bigint
适用于高达64个变量。)
=
运算符)。varbit
或boolean
慢/不方便。如果您有更多的变量,或者想要大量操作值,或者没有巨大的表格或磁盘空间/RAM不是问题,或者不确定选择什么,我会考虑bit(n)
或bit varying(n)
(简称:varbit(n)
)。
对于只有3个比特信息的情况,单独的boolean
列需要3个字节,一个integer
需要4个字节(可能还需要对齐填充),而一个bit string
则需要6个字节(5 + 1)。
对于32个比特的信息,一个integer
仍然需要4个字节(+ 填充),一个bit string
则需要9个字节(5 + 4),而boolean
列占用32个字节。
GENERATED
列的出现,您可以像这样做(下面的所有代码都可以在fiddle链接中找到):CREATE TABLE test
(
t_id INTEGER GENERATED BY DEFAULT AS IDENTITY,
data TEXT,
bitmask VARBIT(9)
);
但是,有了 GENERATED
列,现在你可以这样做:
CREATE TABLE test
(
t_id INTEGER GENERATED BY DEFAULT AS IDENTITY,
data TEXT,
bitmask VARBIT(9), -- choose 9 because it's not 8, to show that you don't have to
-- select an INT or even a SMALLINT
published BOOLEAN GENERATED ALWAYS AS (GET_BIT(bitmask, 0)::BOOLEAN) STORED,
visible BOOLEAN GENERATED ALWAYS AS (GET_BIT(bitmask, 1)::BOOLEAN) STORED,
rubbish BOOLEAN GENERATED ALWAYS AS (GET_BIT(bitmask, 2)::BOOLEAN) STORED,
masterpiece BOOLEAN GENERATED ALWAYS AS (GET_BIT(bitmask, 3)::BOOLEAN) STORED,
meh BOOLEAN GENERATED ALWAYS AS (GET_BIT(bitmask, 4)::BOOLEAN) STORED,
arts BOOLEAN GENERATED ALWAYS AS (GET_BIT(bitmask, 5)::BOOLEAN) STORED,
legal BOOLEAN GENERATED ALWAYS AS (GET_BIT(bitmask, 6)::BOOLEAN) STORED,
sport BOOLEAN GENERATED ALWAYS AS (GET_BIT(bitmask, 7)::BOOLEAN) STORED,
politics BOOLEAN GENERATED ALWAYS AS (GET_BIT(bitmask, 8)::BOOLEAN) STORED,
CONSTRAINT subject_ck -- so you can't have conflicting subjects - just for demo purposes
CHECK -- a document can't be art and legal at the same time!
(
CASE
WHEN GET_BIT(bitmask, 5) = 1
THEN GET_BIT(bitmask, 6) = 0 AND GET_BIT(bitmask, 7) = 0 AND GET_BIT(bitmask, 8) = 0
WHEN GET_BIT(bitmask, 6) = 1
THEN GET_BIT(bitmask, 5) = 0 AND GET_BIT(bitmask, 7) = 0 AND GET_BIT(bitmask, 8) = 0
WHEN GET_BIT(bitmask, 7) = 1
THEN GET_BIT(bitmask, 6) = 0 AND GET_BIT(bitmask, 5) = 0 AND GET_BIT(bitmask, 8) = 0
WHEN GET_BIT(bitmask, 5) = 1
THEN GET_BIT(bitmask, 8) = 0 AND GET_BIT(bitmask, 7) = 0 AND GET_BIT(bitmask, 5) = 0
END
)
);
VIRTUAL
列时,就根本没有空间开销了。CASE
语句,你只需要执行以下操作:INSERT INTO test (data, bitmask) VALUES
('Document 1', '000100000'),
('Document 2', '100000000'),
('Document 3', '101000001');
CREATE INDEX legal_ix ON test (legal) WHERE legal;
现在,获取所有记录变得更加易读 - 更加清晰易懂:
SELECT * FROM test;
结果:
t_id data bitmask published visible rubbish masterpiece meh arts legal sport politics
1 Document 1 000100000 f f f t f f f f f
2 Document 2 100000000 t f f f f f f f f
3 Document 3 101000001 t f t f f f f f t
BEGIN TRANSACTION; -- can't update the other way round or CHECK constraint will fail
-- CHECK constraints are not deferrable - can't be 8 & 6 simultaneously
UPDATE test
SET bitmask = SET_BIT(bitmask, 8, 0) WHERE data = 'Document 3';
UPDATE test
SET bitmask = SET_BIT(bitmask, 6, 1) WHERE data = 'Document 3';
COMMIT;
结果:
SELECT t_id, published, legal FROM test; -- legal has gone from f -> t
这个示例中还有一些其他的小组件。
integer
列。 - Igor Zinov'yev