PostgreSQL 中的 bit_count 函数

rmu*_*ler 5 postgresql hamming-distance

我们正在将 MySQL 5.7 数据库迁移到 PostgreSQL 9.6。

一个真正的问题是bit_countPostgreSQL缺乏功能。此功能在即将发布的版本 10 中也不可用。

当前 MySQL 代码片段(简化):

-- mysql specific, tested with 5.7.19
select code,phash,bit_count(phash ^ -9187530158960050433) as hd 
from documents 
where phash is not null and bit_count(phash ^ -9187530158960050433) < 7
order by hd;
Run Code Online (Sandbox Code Playgroud)

我们尝试了一个简单的解决方案(将 BIGINT 转换为字符串并计算“1”),但与 MySQL 相比,它的表现非常糟糕。

Java使用了 Hacker's Delight 的一个技巧,但 AFAIK 这在 PostgreSQL 中是不可能的,因为该>>>运算符(也)不可用。

问题:是否有与 MySQL 性能相当的解决方案/解决方法?

更新 1

我能找到的最佳解决方案是基于这个 SO 答案

首先创建bit_count函数:

CREATE OR REPLACE FUNCTION bit_count(value bigint) 
RETURNS numeric 
AS $$ SELECT SUM((value >> bit) & 1) FROM generate_series(0, 63) bit $$
LANGUAGE SQL IMMUTABLE STRICT;
Run Code Online (Sandbox Code Playgroud)

现在我们可以使用与 MySQL 几乎相同的 SQL:

-- postgresql specific, tested with 9.6.5
select code,phash,bit_count(phash # -9187530158960050433) as hd 
from documents 
where phash is not null and bit_count(phash # -9187530158960050433) < 7
order by hd;
Run Code Online (Sandbox Code Playgroud)

更新 2

基于@a_horse_with_no_name 评论,我尝试了这个功能:

-- fastest implementation so far. 10 - 11 x faster than the naive solution (see UPDATE 1)
CREATE OR REPLACE FUNCTION bit_count(value bigint) 
RETURNS integer 
AS $$ SELECT length(replace(value::bit(64)::text,'0','')); $$
LANGUAGE SQL IMMUTABLE STRICT;
Run Code Online (Sandbox Code Playgroud)

然而,这仍然比 MySQL 慢 5 - 6 倍(在相同硬件上用完全相同的 200k phash 值的相同数据集进行测试)。

Sas*_*ann 1

函数 bit_count 从 PostgreSQL 版本 14 开始可用,请参阅 位字符串函数和运算符

例子:

select bit_count(B'1101');
Run Code Online (Sandbox Code Playgroud)

结果是 3。

请注意,该函数是针对位类型和位变化类型定义的。因此,如果您想将其与整数值一起使用,则需要进行强制转换。

例子:

select cast (cast (1101 as text) as bit varying);
Run Code Online (Sandbox Code Playgroud)

结果是 B'1101'。

结合两个例子:

select bit_count(cast (cast (1101 as text) as bit varying));
Run Code Online (Sandbox Code Playgroud)