用一种不吸吮的方式找到MySQL的模糊重复

时间:2014-02-12 23:05:46

标签: mysql fuzzy-search soundex

我看到关于这个常见问题的堆栈有很多问题和潜在的答案。由于操作员输入错误或其他轻微变化,您有多年来可能吸引了多个重复记录的名称或公司或产品表。

我正在尝试创建后端管理工具来管理此问题。所以它不会是一个高流量的页面,但它仍然不应该杀死构建记录集的数据库。

到目前为止,我有这个查询,需要花费几分钟来处理(太长时间):

SELECT 
    tab1.id as id1, 
    tab1.creative as creative1, 
    tab2.id as id2, 
    tab2.creative as creative2 
FROM 
    creatives tab1, 
    creatives tab2 
WHERE 
    SOUNDEX(tab1.creative)= SOUNDEX(tab2.creative) 
AND 
    tab1.id<>tab2.id 
AND 
    tab1.id=(
        SELECT 
            MAX(id) 
        FROM 
            creatives tab 
        WHERE 
            SOUNDEX(tab.creative)=SOUNDEX(tab1.creative))

现在除了花费太长时间才能返回结果之外,结果有时候太模糊了。例如,它找到了这些很好:

Convenery of Trades of Edinburgh | Convenery of Trades of Edinbubrgh
Crowdedlogic Theatre Company | Crowded Logic Theatre Company

但这些似乎不合时宜

Daniel Cope | Dan Willis & Obie
David Williams | David Holmes

那么,有没有更好的(更快,更不模糊)的方式呢?

1 个答案:

答案 0 :(得分:7)

您的问题分为两部分。

首先,为什么你的查询很慢?

其次,SOUNDEX()为什么会有太多的误报?有没有比SOUNDEX()提供近似匹配的更好方法?

让我们一次拿起一个。

首先,让我们尝试加快这个查询。让我们从标准SQL中重新编写它(消除旧式JOIN)。

SELECT 
    tab1.id as id1, 
    tab1.creative as creative1, 
    tab2.id as id2, 
    tab2.creative as creative2 
FROM creatives AS tab1 
JOIN creatives tab2 
  ON (
          tab1.id < tab2.id   /* don't duplicate pairs a/b b/a */  
      AND SOUNDEX(tab1.creative)= SOUNDEX(tab2.creative) 
     )

现在让我们省略查询的最后一个句子。

正如您所看到的,如果您的表中有n行,这将按(n平方)次数的顺序评估SOUNDEX函数。

我建议你在表格中添加一个新列。将它设为文本字符串。称之为compare_hash。

然后像这样填充:

UPDATE creatives 
   SET compare_hash = SOUNDEX(creative)

然后将其编入索引。

然后运行此查询:

    SELECT 
    tab1.id as id1, 
    tab1.creative as creative1, 
    tab2.id as id2, 
    tab2.creative as creative2 
FROM creatives AS tab1 
JOIN creatives tab2 
  ON (
          tab1.id < tab2.id   /* don't duplicate pairs a/b b/a */  
      AND tab1.compare_hash = tab2.compare_hash
     )

这应该快一点,因为它可以使用索引。

关于你的第二个问题。看,这是交易:SOUNDEX()旨在获得大量误报。它也是为美国英语名字设计的。这是一个老式的贝尔系统电话公司信息操作员功能,当人们要求Bessie Schmidt想要Bessie Smith时,它会显示多个名字。

您需要烹饪一些其他比较哈希函数并进行实验。关于你的额外表格列很酷的事情就是你可以这样做。此示例将所有字符串转换为小写,然后拉出空格,然后拉出每个元音。因此,“David Williams”的散列将是“dvdwllms”,这与“dvdhlms”不同

UPDATE creatives  SET compare_hash = LOWER(creative);
UPDATE creatives  SET compare_hash = REPLACE(compare_hash , ' ', '');
UPDATE creatives  SET compare_hash = REPLACE(compare_hash , 'a', '');
UPDATE creatives  SET compare_hash = REPLACE(compare_hash , 'e', '');
UPDATE creatives  SET compare_hash = REPLACE(compare_hash , 'i', '');
UPDATE creatives  SET compare_hash = REPLACE(compare_hash , 'o', '');
UPDATE creatives  SET compare_hash = REPLACE(compare_hash , 'u', '');

完成compare_hash之后,您可以运行相同的自连接查询。

(我已经尝试过Levenshein距离。这个问题就是为不同长度的字符串对获得一致的相同度量。)

这需要一些麻烦和一些肘部油脂才能完成这项工作。你在编程中编程了多少取决于http://xkcd.com/1205/