В моем приложении мне нужно идентифицировать человека, ища его фамилию и имя. Одно из требований - допускать орфографические ошибки в определенной степени .
Мои попытки идентифицировать человека по имени и фамилии были:
- SQL-запрос с использованием soundex
- sql-запрос с использованием levenshtein-distance (LD), который был рассчитан с помощью этой LD-функции
На скриншоте содержатся некоторые записи теста и результат моего sql-запроса, который включает значение soundex для каждого столбца и LD
![compare records using levenshtein distance](https://i.stack.imgur.com/vInKH.png)
Мой текущий запрос выглядит так
SELECT t2.*
, t1.Firstname + ' ' + t1.Lastname as SourceName
, 'Torsten Mueller' as TargetName
, dbo.FUNC_LEVENSHTEIN(t1.Firstname +' '+ t1.Lastname
, 'Torsten Mueller', 8) as LEVENSHTEIN_Distance
FROM #TestSoundex t1
LEFT JOIN #TestSoundex t2 ON t1.Id = t2.Id
WHERE t1.Soundex_Firstname = SOUNDEX('Torsten')
AND t1.Soundex_Lastname = SOUNDEX('Mueller')
Как вы видите, я сначала фильтрую результат по soundex и вычисляю расстояние Левенштейна для оставшихся записей. В этом примере ниже расстояние Левенштейна варьируется от 0 (обе строки равны) до 3.
SourceName | TargetName | Levenshtein Distance
Thorsten Müller | Torsten Mueller | 3
Torsten Müller | Torsten Mueller | 2
Thorsten Mueller | Torsten Mueller | 1
Torsten Mueller | Torsten Mueller | 0
В этом выступлении профессора Стэнфорда поясняется расчет расстояния:
I N T E * N TION
| | | | | | |
* E X E C U TION
d s s i s
Каждое удаление d
, вставка i
добавляет 1 балл, замена s
добавляет 2 балла.
LD-function
, который я использую, возвращает 5 точек для примера выше, но только 3 вместо 4 для расстояния между Thorsten Müller
и Torsten Mueller
.
I
+1 point to delete h,
+1 point instead of 2 to substitute ü
+1 point to insert e
Итак, я добавил несколько образцов
![Samples with Umlaut](https://i.stack.imgur.com/P97cv.png)
Вопросы
У меня сложилось впечатление, что ни soundex, ни LD недостаточно для однозначной идентификации записи человека с учетом firstname
и lastname
и с учетом того, что могут быть несоответствия правописания.
- Можете ли вы объяснить, как эта функция LD обрабатывает Umlaute
ü,ö,ä
, чтобы я мог лучше понять вычисления?
- Что бы вы порекомендовали в качестве максимального значения для
distance
, чтобы найти правильное совпадение имени и фамилии с учетом строки s
и t
, должно ли оно основываться на длине обеих строк numberOrCharacters(s+t)/2 = max
?
Исходный код
Это функция, которую я использую из связанного ответа . Я только изменил название функции с edit_distance_within
на FUNC_LEVENSHTEIN
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_NULLS ON
GO
CREATE FUNCTION FUNC_LEVENSHTEIN(@s nvarchar(4000), @t nvarchar(4000), @d int)
RETURNS int
AS
BEGIN
DECLARE @sl int, @tl int, @i int, @j int, @sc nchar, @c int, @c1 int,
@cv0 nvarchar(4000), @cv1 nvarchar(4000), @cmin int
SELECT @sl = LEN(@s), @tl = LEN(@t), @cv1 = '', @j = 1, @i = 1, @c = 0
WHILE @j <= @tl
SELECT @cv1 = @cv1 + NCHAR(@j), @j = @j + 1
WHILE @i <= @sl
BEGIN
SELECT @sc = SUBSTRING(@s, @i, 1), @c1 = @i, @c = @i, @cv0 = '', @j = 1, @cmin = 4000
WHILE @j <= @tl
BEGIN
SET @c = @c + 1
SET @c1 = @c1 - CASE WHEN @sc = SUBSTRING(@t, @j, 1) THEN 1 ELSE 0 END
IF @c > @c1 SET @c = @c1
SET @c1 = UNICODE(SUBSTRING(@cv1, @j, 1)) + 1
IF @c > @c1 SET @c = @c1
IF @c < @cmin SET @cmin = @c
SELECT @cv0 = @cv0 + NCHAR(@c), @j = @j + 1
END
IF @cmin > @d BREAK
SELECT @cv1 = @cv0, @i = @i + 1
END
RETURN CASE WHEN @cmin <= @d AND @c <= @d THEN @c ELSE -1 END
END
GO
Источник для проверки функции выше
Это еще один тест
CREATE TABLE #TestLevenshteinDistance(
Id int IDENTITY(1,1) NOT NULL,
SourceName nvarchar(100) NULL,
Soundex_SourceName varchar(4) NULL,
Targetname nvarchar(100) NULL,
Soundex_TargetName varchar(4) NULL,
);
INSERT INTO #TestLevenshteinDistance
( SourceName,
Soundex_SourceName,
Targetname,
Soundex_TargetName)
VALUES
('Intention',SOUNDEX('Intention'), 'Execution', SOUNDEX('Execution')),
('Karsten' , SOUNDEX('Karsten'), 'Torsten', SOUNDEX('Torsten'));
SELECT t1.*
, dbo.FUNC_LEVENSHTEIN(t1.SourceName, t1.Targetname, 8) as LEVENSHTEIN_Distance
FROM #TestLevenshteinDistance t1