6

我有一个不同的位掩码字段,我想对其执行按位与。

PG::Error: ERROR:  cannot AND bit strings of different sizes
SELECT "groups".* FROM "groups"  WHERE (read_roles_bitmask = B'0' OR read_roles_bitmask & B'10' > B'0')

(你需要在你的表中有不同长度的位掩码才能得到这个错误。)

我希望按位数学如下所示:00010 & 100000010 = 00010

我还尝试将位掩码转换为整数,但没有成功。

为什么 PostgreSQL 对此感到窒息?

我应该如何重写这个查询才能很好地发挥作用?

我能够使用以下方法使按位运算符工作: lpad(read_roles_bitmask::varchar,64,'0')::bigint

但是这仅限于64位,有更好的方法吗?

4

3 回答 3

9

PostgreSQLbitbit varying类型的行为是非常无用的,它拒绝为操作扩展位域,并且它右扩展它们以进行强制转换而不是左扩展它们。

Pg 在 AND 或 OR 操作之前将较小的操作数左扩展为零是有意义的,而不是失败。

您不能使用强制转换bit(n)来获得相同的长度,因为出于某种疯狂的原因,强制转换会bit(n) 右填充参数,使其在几乎所有情况下都无用。

您可以使用类似lpad($1::text, greatest(length($1), length($2)),'0')::bit varying将带有零的位字段左扩展为两个长度中的较大者。这很麻烦,但它会起作用。我建议编写包装函数来包含混乱。

或者,考虑修改bit支持代码src/backend/utils/adt/varbit.c以向左扩展和左截断位字段添加函数,以及进行左扩展比较的函数。根据现有代码,它应该很容易。

于 2012-12-04T01:02:42.870 回答
2

我今天有一个类似的问题。我想做几乎完全相同的事情:屏蔽位串的最低有效两位并将结果与​​文字值进行比较,如下所示:

status & b'11' > b'01'

(状态是我有点变化的专栏)。

最初我尝试使用 Craig 的解决方案,但它很快就变得非常混乱,因为不仅掩码必须保持扩展,我将结果与之比较的值也是如此,因为根据 postgresql:

t2=> select b'0010' < b'01';
 ?column?
----------
 t
(1 row)

<在应用操作之前,RHS 被正确填充以使其大小与 LHS 相同。

最后我这样解决了:

(status << length(status)-2)::bit(2) > b'01'

这样做的好处是它允许您提取任何一组位进行比较。例如,要获得左起第 3 个位对:

(status << length(status)-6)::bit(2)

您还可以使用substring提取任意一组位进行比较。

于 2015-08-26T01:33:59.180 回答
1

1)如其他答案中所述 -

postgres has has some inconvenient/counterintuitive behaviors - 
right padding when casting to bit(n), 
bitwise ops only on similar size,
etc.

2)一种解决方法是 -

double-casting every value - to integer and then to bit(XX)

优点:

- left vs right padding works correctly 
- all the bit-strings have same length for correct bitwise operations
- comparisons work correctly
- bit-masking/casting to get least significant bits

例子:

基本左侧填充:

select B'0010'::int::bit(22)
0000000000000000000010

位运算:

select B'0010'::int::bit(22) | B'01'::int::bit(22)
0000000000000000000011

比较:

select B'0010'::int::bit(22) > B'01'::int::bit(22)
true

位掩码/转换以获得三个最低有效位:

select B'11010'::int::bit(3)
010

位掩码/转换以获得三个最重要的位:

select B'11010'::bit(3)
110

更新: 使用 int8 来容纳更长的位串

于 2019-11-18T16:00:00.507 回答