Postgres中针对零的通用位串比较

时间:2013-11-28 00:32:45

标签: sql postgresql types bit-manipulation

有没有办法在不对位串宽度为0进行硬编码的情况下进行非零位串测试?

例如,假设我有两个表,即用户和功能,每个都带有掩码,我想测试一下:

SELECT u.name FROM Users u, Features f
  WHERE u.mask & f.mask;

匹配隐式非零结果。但是,SQL需要WHERE的显式布尔结果,而不是隐式转换,例如:

SELECT u.name FROM Users u, Features f
  WHERE (u.mask & f.mask) != 0::BIT(2048);

出于多种原因,我不想在此查询中对2048(或其他)进行硬编码。

测试expr = 0expr > 0会导致类型错误。奇怪的是,我可以测试expr = 0::BIT(1),但这给出了错误的答案,因为Postgres并不认为所有的全零位字符串都是相等的。

select 0::BIT(2) > 0::BIT(1);
 ?column? 
----------
 t
(1 row)

我可以通过这样做来创建计算零:

SELECT u.name FROM Users u, Features f
  WHERE (u.mask & f.mask) != (u.mask & ~u.mask);

哪个有效,但感觉就像一个可怕的黑客。

有任何建议或见解吗?

结果

我对下面提供的几个选项进行了基准测试。感谢您的建议,Erwin!

基于非常大的数据集和100,000个查询,我发现以下结构导致每秒相关的查询。希望Postgres团队中的某个人看到这一点并提供通用0以加快速度!不幸的是,大多数通用方法似乎都会导致字符串转换非常昂贵。

Constructs                              |  Queries / s
----------------------------------------+--------------
(u.mask & f.mask) <> 0::BIT(2048)       |  158
(u.mask & f.mask) <> (u.mask # u.mask)  |  135
(u.mask & f.mask) <> (u.mask & ~u.mask) |  125
position('1' IN (u.mask & f.mask)) > 0  |   37
(u.mask & f.mask)::TEXT !~ '^0+$'       |   27

1 个答案:

答案 0 :(得分:4)

短位串

要排除bitwise AND (&)返回的字符串只包含零而非长度可能发生更改(B'000...')的情况,您可以使用强制转换为integer(最多{ {1}})或bit(32)(最多bigint):

bit(64)

当转换为整数时,所有这些都会导致SELECT u.name FROM users u JOIN features f ON (u.mask & f.mask)::int <> 0; 这也排除了其中任一列为0的情况。换句话说,结果必须包含至少一个NULL

长位串

如果您的值可能超过64位,则可以转换为1并使用正则表达式检查:

text

模式解释:

ON (u.mask & f.mask)::text !~ '^0+$' ..字符串的开头
^ ..一个或多个'0'
0+ ..字符串结尾

或者,the manual informs

  

以下SQL标准函数适用于位字符串以及   字符串:$lengthbit_lengthoctet_lengthpositionsubstring

埃尔戈:

overlay