【问题标题】:PostgreSQL: Using BIT VARYING column for bitmask operationsPostgreSQL:使用 BIT VARYING 列进行位掩码操作
【发布时间】:2021-01-13 23:47:30
【问题描述】:

多年来,作为一名 C 程序员,我一直在使用位掩码,我正试图在 Postgres 中做类似的事情,但它并没有像我预期的那样工作。所以这是一个有 2 列的表定义:

    dummy 
    ( 
        countrymask  BIT VARYING (255) not null,  -- Yes it's a pretty wide bitmask
        countryname  CHARACTER VARYING NOT NULL, 
    );

因此,“虚拟”表中的一些数据将是:

现在,使用掩码单选返回阿尔巴尼亚、亚美尼亚和白俄罗斯的 SQL 是什么? (即'100010001')

我以为会是这样的:

SELECT * FROM DUMMY WHERE (countrymask & (b'100010001')) <> 0;

但我遇到了类型不匹配的问题。我希望得到一些帮助。 但是,当类型转换被整理出来时,这会起作用吗?

【问题讨论】:

  • 关系数据库不是 C 程序。使用位掩码并不适合关系模型。正确规范化的模型将更易于使用。 create table dummy (id integer primary key, country_name varchar(100))
  • 好吧,我们一直对表进行非规范化以提高性能,因此雪花模式等。该数据库中有数百万行,我们希望根据需要选择不同的国家/地区集。所以该集合中包含所有国家名称 - 大约 200 个独立国家。使用标准的“IN”子句非常慢,尤其是使用国家名称(文本)会强制进行多个表扫描。欢迎对此提出任何想法。
  • 我看不出 IN 子句会如何导致 多个 表扫描。

标签: sql postgresql select bitmask


【解决方案1】:

您必须始终使用相同长度的位串,即bit(255),并存储所有前导零。

如果你可以使用整数,这会更简单

WHERE countrymask & 273 <> 0

但没有支持&amp; 运算符的 255 位整数类型。

无论如何,这样的查询永远不会使用索引,这对于像dummy 这样的小表没有问题,但如果您想扫描更大的表,则可能会出现问题。

在某种程度上,该数据模型违反了第一范式,因为它将多个国家/地区代码存储在一个数据中。我认为你会更喜欢经典的关系模型:有一个国家表,它的数字主键填充了一个序列,并使用映射表将另一个表中的行与多个国家/地区相关联。

另一种方法是将国家/地区存储为国家标识符数组 (bigint[])。然后,您可以使用“重叠”运算符&amp;&amp; 来扫描表中包含给定数组中的任何国家/地区的行。这样的操作can be made fast with a GIN index

【讨论】:

  • 很好的回应,谢谢劳伦兹。我面临的问题是该表有超过 20M 行,并带有一个“国家”列。应用程序/用户可以选择世界上的任何国家 - 如上例所示,应用程序将返回与之对应的所有行。在 SQL 中使用“IN”子句会导致多次表扫描。所以位掩码似乎是解决这个问题的一种方法。欢迎任何关于更好机制的想法。
  • 我在答案中添加了一个建议。数组可以做你想做的事。
  • 谢谢 Laurenz,我想这就是我需要的答案。作为参考,在我阅读了您的帖子后,我去了这个页面查看了一个实现。 hashrocket.com/blog/posts/exploring-postgres-gin-index
最近更新 更多