【问题标题】:Fuzzy lookup in SQL to match namesSQL 中的模糊查找以匹配名称
【发布时间】:2020-02-10 10:53:00
【问题描述】:

我遇到了一个问题,我需要使用模糊匹配填充历史数据。我正在使用 SQL Server 2014 开发人员版

MainTbl.UNDERWRITER_CODE 是需要填充数据以代替 NULL 的位置。此数据需要来自LKP 表。匹配标准是MainTbl.UNDERWRITER_NAMELKP.UNDERWRTIER_NAME

样本:

CREATE TABLE MainTbl(UNDERWRITER_CODE int,  UNDERWRITER_NAME varchar(100))
INSERT INTO MainTbl VALUES
(NULL,'dylan.campbell'),
(NULL,'dylanadmin'),
(NULL,'dylanc'),
(002,'Dylan Campbell'),
(002,'dylan.campbell'),
(002,'dylanadmin'),
(NULL,'scott.noffsinger'),
(001,'Scott Noffsinger')


CREATE TABLE LKP(UNDERWRITER_CODE int,  UNDERWRITER_NAME varchar(100))
INSERT INTO LKP VALUES
(002,'Dylan Campbell'),
(001,'Scott Noffsinger')

预期输出:

2 dylan.campbell
2 dylanadmin
2 dylanc
2 Dylan Campbell
2 dylan.campbell
2 dylanadmin
1 scott.noffsinger
1 Scott Noffsinger

【问题讨论】:

  • @MattEvans:我应该如何实现它?可以简单分享一下吗?我有大约十万行需要受到影响
  • 老实说,这并不是 SQL Server 的真正强项。你可以使用SOUNDEXDIFFERENCE 之类的东西。也许依赖于DIFFERENCEreturns a value of 3 or 4?我们真的没有很多样本可以作为基础。
  • 如果您的查找中有“Dylan Smith”怎么办? dylanadmin 被分配到什么位置?
  • 我要检查的第一件事:使用GROUP BY 找出代码为NULL 的UNDERWRITER_NAME 的所有变体。我会将此列表推送到映射表中,并以半自动方式(自动化和手动更正的混合)找到一个常见的表达式。最后我会使用这个清理过的映射来进行 JOIN。

标签: sql sql-server tsql fuzzy-logic


【解决方案1】:

SQL 并不是真正为这种模糊字符串比较而设计的。但是,SQL Server 有一个名为 difference() 的函数,它适用于您的数据:

select mt.*, l.*
from maintbl mt outer apply
     (select top (1) lkp.*
      from lkp
      order by difference(mt.underwriter_name, lkp.underwriter_name) desc
     ) l;

Here 是一个 dbfiddle。

【讨论】:

  • 谢谢。但它打破了许多记录(上面的例子中没有提到)。猜猜这是因为输出的差异。
  • @PrabhatG 。 . .阅读答案的第一句话。
【解决方案2】:
UPDATE T1 SET T1.UNDERWRITER_CODE = T2.UNDERWRITER_CODE
FROM MainTbl T1 
INNER JOIN LKP T2
ON T1.UNDERWRITER_NAME LIKE CONCAT('%', LEFT( LOWER(T2.UNDERWRITER_NAME)
                                             ,CHARINDEX(' '
                                                        ,LOWER(T2.UNDERWRITER_NAME)
                                                       ) - 1
                                            )
                                      , '%'
                                   )

输出

https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=23a3a55cc1ab1741f6e70dd210db0471

说明

第 1 步:

SELECT *
       ,CONCAT('%', LEFT( LOWER(T2.UNDERWRITER_NAME)
                                             ,CHARINDEX(' '
                                                        ,LOWER(T2.UNDERWRITER_NAME)
                                                       ) - 1
                                            )
                                      , '%'
                                   ) AS JOIN_COL
FROM LKP T2

上述查询的输出

UNDERWRITER_CODE    UNDERWRITER_NAME    JOIN_COL
2                   Dylan Campbell      %dylan%
1                   Scott Noffsinger    %scott%

在连接条件中使用上述 JOIN_COL 数据格式与 like 运算符 第 2 步:

SELECT T2.UNDERWRITER_CODE,T1.UNDERWRITER_NAME
FROM MainTbl T1
INNER JOIN LKP T2
ON T1.UNDERWRITER_NAME LIKE CONCAT('%', LEFT( LOWER(T2.UNDERWRITER_NAME)
                                             ,CHARINDEX(' '
                                                        ,LOWER(T2.UNDERWRITER_NAME)
                                                       ) - 1
                                            )
                                      , '%'
                                   )

上述查询的输出:

UNDERWRITER_CODE    UNDERWRITER_NAME
2                   dylan.campbell
2                   dylanadmin
2                   dylanc
2                   Dylan Campbell
2                   dylan.campbell
2                   dylanadmin
1                   scott.noffsinger
1                   Scott Noffsinger

【讨论】:

  • 你能解释一下你的加入逻辑吗?
  • @PrabhatG 你能到上面的链接解释吗
  • 这如何解释逻辑,@JayShankarGupta?
  • @PrabhatG 补充说明
【解决方案3】:

首先,模糊查找有点模糊。有许多算法可用于模糊匹配,包括 Levenshtein 距离、最长公共子序列,以及此 Wikipedia 页面 about Approximate String Matching 的“另见”部分中引用的其他一些算法。

改写你正在尝试做的事情。您正在使用与LKP最相似 UNDERWRITER_NAME 匹配的UNDERWRITER_CODE 更新MainTbl 中的UNDERWRITER_CODE 列。模糊算法可用于测量相似度。注意我的帖子here。对于您提供的示例数据,我们可以使用 Phil Factor 的 T-SQL Levenshtein functions 并根据最低 Levenshtein 值进行匹配,如下所示:

SELECT TOP (1) WITH TIES
  UNDERWRITER_CODE_NULL = m.UNDERWRITER_CODE,
  LKP_UN = m.UNDERWRITER_NAME, l.UNDERWRITER_NAME, l.UNDERWRITER_CODE,
  MinLev = dbo.LEVENSHTEIN(m.UNDERWRITER_NAME, l.UNDERWRITER_NAME)
FROM       dbo.MainTbl AS m
CROSS JOIN dbo.LKP     AS l
WHERE      m.UNDERWRITER_CODE IS NULL
ORDER BY ROW_NUMBER() OVER (PARTITION BY m.UNDERWRITER_NAME
                      ORDER BY dbo.LEVENSHTEIN(m.UNDERWRITER_NAME, l.UNDERWRITER_NAME))

返回:

UNDERWRITER_CODE_NULL LKP_UN             UNDERWRITER_NAME   UNDERWRITER_CODE MinLev
--------------------- ------------------ ------------------ ---------------- -----------
NULL                  dylan.campbell     Dylan Campbell     2                1
NULL                  dylanadmin         Dylan Campbell     2                8
NULL                  dylanc             Dylan Campbell     2                8
NULL                  scott.noffsinger   Scott Noffsinger   1                1

我们可以像这样使用这个逻辑来更新UNDERWRITE_CODE

WITH FuzzyCompare AS
(
    SELECT TOP (1) WITH TIES
      UNDERWRITER_CODE_NULL = m.UNDERWRITER_CODE,
      LKP_UN = m.UNDERWRITER_NAME, l.UNDERWRITER_NAME, l.UNDERWRITER_CODE,
      MinLev = dbo.LEVENSHTEIN(m.UNDERWRITER_NAME, l.UNDERWRITER_NAME)
    FROM       dbo.MainTbl AS m
    CROSS JOIN dbo.LKP     AS l
    WHERE      m.UNDERWRITER_CODE IS NULL
    ORDER BY ROW_NUMBER() OVER (PARTITION BY m.UNDERWRITER_NAME
                          ORDER BY dbo.LEVENSHTEIN(m.UNDERWRITER_NAME, l.UNDERWRITER_NAME))
)
UPDATE fc
SET    fc.UNDERWRITER_CODE_NULL = fc.UNDERWRITER_CODE
FROM   FuzzyCompare AS fc
JOIN   dbo.MainTbl  AS m ON fc.UNDERWRITER_NAME = m.UNDERWRITER_NAME;

本次更新后SELECT * FROM dbo.mainTbl返回:

UNDERWRITER_CODE UNDERWRITER_NAME
---------------- -------------------
2                dylan.campbell
2                dylanadmin
2                dylanc
2                Dylan Campbell
2                dylan.campbell
2                dylanadmin
1                scott.noffsinger
1                Scott Noffsinger

这应该让你开始;根据您处理的数据的数量和类型,您需要对使用的算法非常有选择性。做好功课,测试,测试,测试!

如果您有任何问题,请告诉我。

【讨论】:

    猜你喜欢
    • 2010-10-10
    • 2021-10-05
    • 1970-01-01
    • 2012-03-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-22
    相关资源
    最近更新 更多