SQL中二进制字符串的汉明距离

SQL中二进制字符串的汉明距离,sql,mysql,hash,binary-data,hamming-distance,Sql,Mysql,Hash,Binary Data,Hamming Distance,我在数据库中有一个表,在其中我将SHA256哈希存储在一个二进制(32)列中。我正在寻找一种方法来计算列中条目到提供值的汉明距离,例如: SELECT * FROM table ORDER BY HAMMINGDISTANCE(hash, UNHEX(<insert supplied sha256 hash here>)) ASC LIMIT 10 从表中选择* 按汉明距离排序(散列,UNHEX())ASC 限制10 (如果您想知道,字符串A和B的汉明距离定义为位计数

我在数据库中有一个表,在其中我将SHA256哈希存储在一个二进制(32)列中。我正在寻找一种方法来计算列中条目到提供值的汉明距离,例如:

SELECT * FROM table 
  ORDER BY HAMMINGDISTANCE(hash, UNHEX(<insert supplied sha256 hash here>)) ASC 
  LIMIT 10
从表中选择*
按汉明距离排序(散列,UNHEX())ASC
限制10
(如果您想知道,字符串A和B的汉明距离定义为
位计数(A^B)
,其中^是按位异或运算符,位计数返回二进制字符串中的1数)

现在,我知道^operator和BIT_COUNT函数只对整数起作用,所以我想说,可能唯一的方法是将二进制字符串分解成子字符串,将每个二进制子字符串转换为整数,按子字符串计算汉明距离,然后将它们相加。问题是这听起来非常复杂,效率不高,而且绝对不优雅。因此,我的问题是:你能提出更好的办法吗?(请注意,我使用共享主机,因此无法修改DB服务器或加载库)

edit(1):显然,在PHP中加载整个表并进行计算是可能的,但我宁愿避免它,因为这个表可能会变得非常大

编辑(2):数据库服务器是MySQL 5.1

编辑(3):我下面的答案包含我刚才描述的代码


edit(4):我刚刚发现,使用4个bigint而不是二进制(32)来存储散列,可以极大地提高速度(快100多倍)。请参阅下面对我的答案的评论。

有趣的问题,我找到了一种方法,可以对
二进制(3)
执行此操作,这种方法也可以对
二进制(32)
执行:

replace
删除所有零,余数的长度为一的数目。(到二进制的转换忽略了前导零,因此计算零不起作用。)

这将打印
6
,它与中的编号匹配

0xAAAAAA ^ 0x888888 = 0x222222 = 0b1000100010001000100010

似乎将数据存储在
二进制
列中是一种性能较差的方法。获得良好性能的唯一快速方法是将
BINARY
列的内容拆分为多个
BIGINT
列,每个列包含原始数据的8字节子字符串

在我的情况下(32字节),这意味着使用4
BIGINT
列并使用此函数:

CREATE FUNCTION HAMMINGDISTANCE(
  A0 BIGINT, A1 BIGINT, A2 BIGINT, A3 BIGINT, 
  B0 BIGINT, B1 BIGINT, B2 BIGINT, B3 BIGINT
)
RETURNS INT DETERMINISTIC
RETURN 
  BIT_COUNT(A0 ^ B0) +
  BIT_COUNT(A1 ^ B1) +
  BIT_COUNT(A2 ^ B2) +
  BIT_COUNT(A3 ^ B3);
在我的测试中,使用这种方法比使用
BINARY
方法快100多倍


FWIW,这是我在解释问题时暗示的代码。欢迎使用更好的方法来完成同样的事情(我尤其不喜欢二进制>十六进制>十进制转换):


如果这对找到更好的解决方案很有用的话,也可以自由地建议不同的散列存储方法。如果将散列存储在8个整数中(可能除了二进制存储之外),计算会变得容易得多。我真的很好奇为什么要计算距离:)我正在处理一个分布式散列表,这一步需要在其他节点上查找密钥(即,如果您在本地没有密钥,则将查询转发到ID更接近密钥的节点,其中,根据汉明距离来度量更接近的节点);很明显,ID和键的长度是相同的(在我的例子中是256位)。你所说的“表可能会变得相当大”是什么意思?使用足够的函数进行完全扫描是可以接受的,还是需要一种方法来使用一些索引并避免完全扫描?我刚刚运行了一些测试:在一个包含100000行的表上使用这里定义的函数运行原始问题中的查询大约需要2.5秒。因为我不需要查询的确切答案,所以我可以通过添加WHERE RAND()<0.05(随机抽取表的5%)来对表进行采样,这样可以将时间减少到0.2秒。尽管如此,如果一些SQL专家能指出更好的方法,我还是很乐意听到。其他测试:我创建了一个视图,将每个二进制(32)转换为四个bigint。这将时间从2.5s降低到了0.6s。好的,我发现如果我实际使用一个表,其中我将散列存储为4个bigint,那么相同的查询将在0.02s内完成。明确使用二进制(32)是个坏主意(TM)。大家好,我也在尝试用mysql实现汉明距离。如何准确地使用哈希字符串查询相关记录?您是否尝试过使用位列类型?
CREATE FUNCTION HAMMINGDISTANCE(
  A0 BIGINT, A1 BIGINT, A2 BIGINT, A3 BIGINT, 
  B0 BIGINT, B1 BIGINT, B2 BIGINT, B3 BIGINT
)
RETURNS INT DETERMINISTIC
RETURN 
  BIT_COUNT(A0 ^ B0) +
  BIT_COUNT(A1 ^ B1) +
  BIT_COUNT(A2 ^ B2) +
  BIT_COUNT(A3 ^ B3);
CREATE FUNCTION HAMMINGDISTANCE(A BINARY(32), B BINARY(32))
RETURNS INT DETERMINISTIC
RETURN 
  BIT_COUNT(
    CONV(HEX(SUBSTRING(A, 1,  8)), 16, 10) ^ 
    CONV(HEX(SUBSTRING(B, 1,  8)), 16, 10)
  ) +
  BIT_COUNT(
    CONV(HEX(SUBSTRING(A, 9,  8)), 16, 10) ^ 
    CONV(HEX(SUBSTRING(B, 9,  8)), 16, 10)
  ) +
  BIT_COUNT(
    CONV(HEX(SUBSTRING(A, 17, 8)), 16, 10) ^ 
    CONV(HEX(SUBSTRING(B, 17, 8)), 16, 10)
  ) +
  BIT_COUNT(
    CONV(HEX(SUBSTRING(A, 25, 8)), 16, 10) ^ 
    CONV(HEX(SUBSTRING(B, 25, 8)), 16, 10)
  );