在某些不完整的数据中查找单个用户记录


Finding a single user record in some incomplete data?

我正在构建一个用户必须注册的系统。 这些用户也可能是较大客户数据库的一部分,我想将注册与较大客户数据库中的用户 ID 相关联。

客户数据库有些不完整。 有些客户只有一个电话号码,根据谁/什么进入它,它可能在不同的地方有空间。 其他客户只有一个电子邮件地址,由于它是手写的,然后在以后由其他人处理,因此很可能会有拼写错误。 真的是一场噩梦。

我想找到最接近用户在我正在构建的系统上输入的记录。 这些数据非常简单,并且将得到验证。 该数据:

  • 名字
  • DOB (YYYY-MM-DD)
  • 电子邮件地址
  • 电话号码

我最初的想法是使用 Levenshtein 距离算法来计算每个字段的"字符串距离",除非它们是空的,然后按总分排序。 下面的代码中没有显示,以保持美观和可读性,但我显然也会修剪(甚至可能只是删除)所有空格。

作为伪代码:

SELECT c.customerID
FROM   customers c
WHERE  ( c.first_name IS NULL OR ( Levenshtein(c.first_name, $first_name) < 3 ) )
AND    ( c.last_name IS NULL OR ( Levenshtein(c.last_name, $last_name) < 3 ) )
AND    ( c.email IS NULL OR ( Levenshtein(c.email, $email) < 3 ) )
AND    ( c.telephone IS NULL OR ( Levenshtein(c.telephone, $telephone) < 3 ) )

仅供参考,我正在为这两个数据库使用 PHP(Laravel)和 MySQL。

我在这里走在正确的轨道上还是应该使用Levenshtein以外的东西? 我应该比较所有字段分数的某种组合吗?

这条歌绝对是对的,但我会添加一些注释。

准备数据

首先,为了匹配,我建议转换数据以消除可能的噪音,例如将字符串转换为大写,删除空格,从电话号码中删除所有非数字等。

在不完整的数据中查找最接近的匹配项

其次,设置任意阈值(如上面的"小于3")会使其有点僵硬。尽管对 CPU 的要求更高,但最好按"差异因子"对结果进行排序:

SELECT c.customerID
FROM   customers c
ORDER BY
Levenshtein(c.first_name, $first_name)) +
Levenshtein(c.last_name, $last_name) +
Levenshtein(c.email, $email) +
Levenshtein(c.telephone, $telephone) asc
LIMIT 0,1;

显然,当差异高得离谱时,您可以添加一些安全性来不匹配,但您明白了。如果两个对象都在同一字段中缺少数据(例如,两者都缺少电子邮件),该方法仍然可以。 当只缺少一侧时就会出现问题 - 然后我们会为差异而大加赞赏。我们可能会使查询进一步复杂化以避免它:

ORDER BY
(if(c.first_name is null OR c.first_name = '' OR $first_name = '', 0, Levenshtein(c.first_name, $first_name))) +
...

简洁起见,缩短为一行 - 我们仅在有数据要比较时才计算列弗距离。

缺点

对于与大于 X 的差异因子匹配的所有记录,您可能会想到某种标志来让人类决定。经过一段时间的审查,我相信你会想出更多的规则来自动化它。