模糊匹配 SQL 中的字符串
Fuzzy matching a string in SQL
我有一个 User
table,它有 id
、first_name
、last_name
、street_address
、city
, state
, zip-code
, firm
, user_identifier
, created_at
, update_at
.
这个 table 有很多重复,例如相同的用户作为新用户输入了多次,例如
id first_name last_name street_address user_identifier
---------------------------------------------------------
11 Mary Doe 123 Main Ave M2111111
---------------------------------------------------------
21 Mary Doe 123 Main Ave M2344455
---------------------------------------------------------
13 Mary Esq Doe 123 Main Ave M1233444
我想知道有没有办法在这个table上做模糊匹配。
基本上我想找到所有具有相同姓名、相同地址但可能略有不同的用户,也许地址相同但公寓号不同,或者有一个中间名和另一个不重复。
我想创建一个连接了 first_name、last_name、street_address 的新列,并对其进行模糊匹配列。
我尝试在连接的 first_name 和 last_name 上使用 levenshtein 距离作为 full_name
但似乎没有赶上有中间名的名字
select * from users
where levenshtein('Mary Doe', full_name) <=1;
我正在使用 Databricks 和 PostgreSQL。
谢谢!
在 postgres 中你可以使用 fuzzystrmatch 包。它提供了一个 levenshtein
函数,即两个文本之间的 returns 距离,然后您可以使用以下示例谓词进行模糊匹配:
where levenshtein(street_address, '123 Main Avex') <= 1
这将匹配所有记录,因为“123 Main Ave”和“123 Main Avex”之间的距离为 1(1 个插入)。
当然,这里的值1
只是一个例子,会进行相当严格的匹配(仅相差一个字符)。您应该使用更大的数字,或者@IVO GELOV 所建议的 - 使用相对距离(距离除以长度)。
如果您发现 Levenshtein ("edit distance") 没有捕捉到您需要的所有匹配项,我强烈建议您查看 pg_tgrm。它。是。惊人的。
postgresql.org/docs/current/pgtrgm.html.
作为为什么要使用三元组的一个例子,他们让你选择 first_name
和 last_name
颠倒的情况,这是一个相对常见的错误。 Levenshtein 不太适合发现这一点,因为它所做的只是将一个字符串转换为另一个字符串,并计算所需的移动次数。当您交换元素时,它们会大大增加距离并使匹配 less 成为可能。例如,假设您有一条记录,其中正确的全名是 "David Adams"。查找姓氏 "Adam" 以及查找名字和姓氏颠倒的情况是很常见的。所以,这是一个简单名称的三种合理形式。与 Postgres trigram 实现相比,Levenshtein 的表现如何?为此,我将 levenshtein(string 1, string 2)
与 similarity(string 1, string 2)
进行了比较。如上所述,Levenshtein 是一种计数,其中较高的分数意味着 较少 相似。为了将分数标准化为 0-1 值,其中 1 = 相同,我将它除以最大全名长度,如上所述,然后从 1 中减去它。最后一点是使数字直接与 [=15 进行比较=] 得分。 (否则,你会得到数字,其中 1 表示相反的东西。)
这里有一些简单的结果,为清楚起见稍微四舍五入了
Row 1 Row 2 Levenshtein() Levensthein % Similarity %
David Adams Adam David 10 9 77
Adam David Adams David 1 91 77
Adams David David Adams 10 9 100
如您所见,similarity()
分数在很多情况下表现更好,即使是这个简单的示例也是如此。再一次,Levenshtein 在一个案例中感觉更好。结合技术并不罕见。如果您这样做,请将比例归一化以减轻您的头痛。
但是,如果您有更清晰的数据作为起点,所有这一切都会变得容易得多。如果您的问题之一是缩写和标点符号不一致,Levenshtein 可能不适合。因此,在重复匹配之前执行地址标准化很有帮助。
就其价值(很多)而言,Postgres 中的三元组可以使用索引。在与 Levenshtein 之类的东西进行更昂贵的比较之前,尝试找到一种通过索引搜索安全地减少候选人的技术可能是一个不错的选择。啊,Levenshtein 的一个技巧是,如果你有一个 target/tolerance,并且存储了字符串的长度,你可以排除太短或太长的字符串,而不用 运行更昂贵的模糊比较。例如,如果您有一个长度为 10 的起始字符串并且只想要最多 2 个转换距离的字符串,那么您就是在浪费时间来测试只有 7 个字符长的字符串等。
请注意,您描述的错误数据输入问题通常归结为
- 用户培训不佳 and/or
- 糟糕的用户体验
一旦清理工作井井有条,就值得回顾一下不良数据是如何进入的。如果你有一组有限的可训练用户,它可以帮助 运行 每晚(等)扫描以检测新的可能的重复项,然后去与生成它们的人交谈。也许他们不知道你可以告诉他们,也许 UI 有你不知道的问题他们可以告诉你。
有一个喜欢的运营商。您是否考虑过这样做?
以下 SQL 语句选择 CustomerName 以 "a" 开头的所有客户:
例子
SELECT * FROM Customers
WHERE CustomerName LIKE 'a%';
我有一个 User
table,它有 id
、first_name
、last_name
、street_address
、city
, state
, zip-code
, firm
, user_identifier
, created_at
, update_at
.
这个 table 有很多重复,例如相同的用户作为新用户输入了多次,例如
id first_name last_name street_address user_identifier
---------------------------------------------------------
11 Mary Doe 123 Main Ave M2111111
---------------------------------------------------------
21 Mary Doe 123 Main Ave M2344455
---------------------------------------------------------
13 Mary Esq Doe 123 Main Ave M1233444
我想知道有没有办法在这个table上做模糊匹配。
基本上我想找到所有具有相同姓名、相同地址但可能略有不同的用户,也许地址相同但公寓号不同,或者有一个中间名和另一个不重复。
我想创建一个连接了 first_name、last_name、street_address 的新列,并对其进行模糊匹配列。
我尝试在连接的 first_name 和 last_name 上使用 levenshtein 距离作为 full_name
但似乎没有赶上有中间名的名字
select * from users
where levenshtein('Mary Doe', full_name) <=1;
我正在使用 Databricks 和 PostgreSQL。
谢谢!
在 postgres 中你可以使用 fuzzystrmatch 包。它提供了一个 levenshtein
函数,即两个文本之间的 returns 距离,然后您可以使用以下示例谓词进行模糊匹配:
where levenshtein(street_address, '123 Main Avex') <= 1
这将匹配所有记录,因为“123 Main Ave”和“123 Main Avex”之间的距离为 1(1 个插入)。
当然,这里的值1
只是一个例子,会进行相当严格的匹配(仅相差一个字符)。您应该使用更大的数字,或者@IVO GELOV 所建议的 - 使用相对距离(距离除以长度)。
如果您发现 Levenshtein ("edit distance") 没有捕捉到您需要的所有匹配项,我强烈建议您查看 pg_tgrm。它。是。惊人的。
postgresql.org/docs/current/pgtrgm.html.
作为为什么要使用三元组的一个例子,他们让你选择 first_name
和 last_name
颠倒的情况,这是一个相对常见的错误。 Levenshtein 不太适合发现这一点,因为它所做的只是将一个字符串转换为另一个字符串,并计算所需的移动次数。当您交换元素时,它们会大大增加距离并使匹配 less 成为可能。例如,假设您有一条记录,其中正确的全名是 "David Adams"。查找姓氏 "Adam" 以及查找名字和姓氏颠倒的情况是很常见的。所以,这是一个简单名称的三种合理形式。与 Postgres trigram 实现相比,Levenshtein 的表现如何?为此,我将 levenshtein(string 1, string 2)
与 similarity(string 1, string 2)
进行了比较。如上所述,Levenshtein 是一种计数,其中较高的分数意味着 较少 相似。为了将分数标准化为 0-1 值,其中 1 = 相同,我将它除以最大全名长度,如上所述,然后从 1 中减去它。最后一点是使数字直接与 [=15 进行比较=] 得分。 (否则,你会得到数字,其中 1 表示相反的东西。)
这里有一些简单的结果,为清楚起见稍微四舍五入了
Row 1 Row 2 Levenshtein() Levensthein % Similarity %
David Adams Adam David 10 9 77
Adam David Adams David 1 91 77
Adams David David Adams 10 9 100
如您所见,similarity()
分数在很多情况下表现更好,即使是这个简单的示例也是如此。再一次,Levenshtein 在一个案例中感觉更好。结合技术并不罕见。如果您这样做,请将比例归一化以减轻您的头痛。
但是,如果您有更清晰的数据作为起点,所有这一切都会变得容易得多。如果您的问题之一是缩写和标点符号不一致,Levenshtein 可能不适合。因此,在重复匹配之前执行地址标准化很有帮助。
就其价值(很多)而言,Postgres 中的三元组可以使用索引。在与 Levenshtein 之类的东西进行更昂贵的比较之前,尝试找到一种通过索引搜索安全地减少候选人的技术可能是一个不错的选择。啊,Levenshtein 的一个技巧是,如果你有一个 target/tolerance,并且存储了字符串的长度,你可以排除太短或太长的字符串,而不用 运行更昂贵的模糊比较。例如,如果您有一个长度为 10 的起始字符串并且只想要最多 2 个转换距离的字符串,那么您就是在浪费时间来测试只有 7 个字符长的字符串等。
请注意,您描述的错误数据输入问题通常归结为
- 用户培训不佳 and/or
- 糟糕的用户体验
一旦清理工作井井有条,就值得回顾一下不良数据是如何进入的。如果你有一组有限的可训练用户,它可以帮助 运行 每晚(等)扫描以检测新的可能的重复项,然后去与生成它们的人交谈。也许他们不知道你可以告诉他们,也许 UI 有你不知道的问题他们可以告诉你。
有一个喜欢的运营商。您是否考虑过这样做?
以下 SQL 语句选择 CustomerName 以 "a" 开头的所有客户: 例子
SELECT * FROM Customers
WHERE CustomerName LIKE 'a%';