Нечеткий поиск в SQL для соответствия имен - PullRequest
0 голосов
/ 10 февраля 2020

Я застрял в проблеме, когда мне нужно заполнить исторические данные, используя нечеткое совпадение. Я использую SQL Server 2014 Developer Edition

MainTbl.UNDERWRITER_CODE, где данные должны заполняться вместо NULL. Эти данные должны быть из таблицы LKP. Критерии соответствия: MainTbl.UNDERWRITER_NAME с LKP.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

Ответы [ 3 ]

1 голос
/ 10 февраля 2020

SQL на самом деле не предназначен для таких нечетких сравнений строк. Тем не менее, SQL Сервер имеет функцию с именем 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;

Здесь - это db <> скрипка.

0 голосов
/ 10 февраля 2020

Во-первых, нечеткий поиск немного расплывчатый. Существует ряд алгоритмов, которые используются для нечеткого сопоставления, включая расстояние Левенштейна, самую длинную общую подпоследовательность и некоторые другие, на которые есть ссылки в разделе «См. Также» этой страницы Википедии о приближенном сопоставлении строк .

Перефразируя то, что вы пытаетесь сделать. Вы обновляете столбец UNDERWRITER_CODE в MainTbl с помощью UNDERWRITER_CODE, который соответствует наиболее похожим UNDERWRITER_NAME в LKP. Нечеткие алгоритмы могут быть использованы для измерения сходства . Обратите внимание на мой пост здесь . Для предоставленных вами образцов данных мы можем использовать функции T- SQL Левенштейна Фила Фактора и сопоставлять их на основе наименьшего значения Левенштейна, например:

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

Мы можем использовать эту логику c для обновления 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

Это должно заставить вас начать ; в зависимости от количества и вида данных, с которыми вы имеете дело, вам нужно будет очень избирательно выбирать используемые вами алгоритмы. Выполняйте домашнее задание и тестируйте, тестируйте, тестируйте!

Дайте мне знать, если у вас есть вопросы.

0 голосов
/ 10 февраля 2020
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
...