Логика сопоставления строк и ранжирования

#sql #sql-server #tsql

Вопрос:

Мне было интересно, может ли кто-нибудь здесь придумать для меня какое-нибудь решение, которое заняло бы 2 строки и сравнило бы их слово за словом, чтобы дать мне процентное совпадение всей строки.

Пример: Если бы я хотел сравнить эти 2 строки

  1. Эльф на полке: Рождественский Мюзикл (Гастроли)
  2. Эльф на полке Музыкальный Балтимор

В SQL, если я сделаю like сравнение, оно не будет совпадать. Но если я смогу разбить каждое слово и сказать, совпадает ли оно, мы увидим, что 6 из 7 слов соответствуют строке 2 строке 1. И тогда можно было бы сказать, что 85% совпадают

Спасибо!

Комментарии:

1. Что касается самого вопроса, вы специально хотите, чтобы он выполнял какую-то математику для сравнения «слов», поэтому элементы, разделенные пробелами?

2. Предположим, что строка 2 была как раз the тогда, когда у вас было бы 100% совпадение. Возможно, вам потребуется уточнить свой показатель.

3. @PeterSmith я бы, вероятно, построил кучу основных слов, чтобы исключить такие, как «и», «the» и т. Д

4. Да, но elf также дал бы вам 100%

5. @petersmith да, я, вероятно, должен был бы сделать так, чтобы было хотя бы определенное количество слов. Может быть, по крайней мере, 4 слова

Ответ №1:

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

 -- Sample strings
DECLARE 
 @string1 VARCHAR(100) = 'The Elf on the Shelf: A Christmas Musical (Touring)',
 @string2 VARCHAR(100) = 'The Elf on the Shelf Musical Baltimore';

--uncomment to test:
--SELECT @string1 = 'Their', @string2 = 'Theirs'

-- Longest Common Subsequence Solution
SELECT Similarity = 1.*LEN(dbo.LongestCommonSubsequence(@string1,@string2))/L2
FROM
(
  SELECT MIN(f.S), MAX(f.S)
  FROM  (VALUES(LEN(@string1)),(LEN(@string2))) AS f(S)
) f(L1,L2);

-- Levenshtein
SELECT Similarity = (1.*L1-Lev)/L2
FROM
(
  SELECT MIN(f.S), MAX(f.S), dbo.LEVENSHTEIN(@string1,@string2)
  FROM  (VALUES(LEN(@string1)),(LEN(@string2))) AS f(S)
) f(L1,L2,Lev);
 

Каждое возвращение:

 Similarity
---------------------------------------
0.62745098039


Similarity
---------------------------------------
0.31372549019
 

За «их» и «их» вы получаете:

 Similarity
---------------------------------------
0.83333333333

Similarity
---------------------------------------
0.66666666666
 

Ответ №2:

Вот одно из возможных решений для получения процента совпадающих слов, это предполагает, что слова совпадают между двумя строками независимо от позиции.

Я ценю, что это может быть не совсем то, что вам нужно, и не делает «похожих» слов, но, надеюсь, соответствует критериям для процентного соответствия. Есть много возможностей для настройки, если это не совсем то, что нужно.

Здесь две строки разбиты на строки и объединены в одну таблицу после удаления общих знаков препинания. Затем функция окна row_number разбивает их на соответствующие слова и подсчитывает каждую пару. Наконец, это учитывается только для совпадающих пар и суммируется с количеством повторяющихся слов, общих для обоих, затем в процентах от более короткой строки.

 declare 
    @s1 varchar(100)='The Elf on the Shelf: A Christmas Musical. (Touring)',
    @s2 varchar(100)='The Elf on the Shelf Musical, Baltimore';

with words as (
    select 1 s, Replace(Replace(Replace(Replace(Replace(value,':',''),',',''),'(',''),')',''),'.','') word
    from String_Split(@s1,' ')
    union all
    select 2, Replace(Replace(Replace(Replace(Replace(value,':',''),',',''),'(',''),')',''),'.','') 
from String_Split(@s2,' ')
), matching as (
  select *, Row_Number() over(partition by word order by s) rn
  from words
), final as (
  select * , Count(*) over(partition by word, s) repeating, Count(*) over() * 1.0 totwords, sum(Iif(s=1,1,0)) over() s1words
  from matching
  outer apply(values(Iif(rn=2 and rn=s,1,0)))x(p)
)
select (Sum (p)   max(case when s=1 and repeating>1 then repeating end))
        / Max(Iif(totwords/s1words>0.5, totwords-s1words, s1words)) * 100 [Matching Words %]
from final
            
 

Здесь 6 слов в каждой строке совпадают, поэтому желаемые 6 составляют 85,7% от более короткой строки из 7 слов.

Пример DB<>Скрипка

Комментарии:

1. это выглядит многообещающе! у меня возникла проблема с запуском этого. я получаю эти ошибки: Msg 195, уровень 15, состояние 10, строка 6 «Перевод» не является распознанным именем встроенной функции. Msg 195, Уровень 15, состояние 10, строка 9 «Перевод» не является распознанным именем встроенной функции. Msg 102, Уровень 15, Состояние 1, Строка 14 Неправильный синтаксис рядом с»,». Msg 102, Уровень 15, Состояние 1, Строка 15 Неверный синтаксис рядом с ‘=’. Msg 102, Уровень 15, Состояние 1, Строка 20 Неправильный синтаксис рядом с»>».

2. @kivi12k Translate появился в SQL Server 2017, вы, должно быть, используете более старую версию SQL Server 2016/2014/2012? Это удобный способ удаления нескольких символов одновременно, в противном случае вы получите кучу вложенных замен (), которые вам, возможно, придется сделать, например replace(replace(@string,':',''),',','') , и т. Д. Для удаления пунктуации.

3. Я использую sql server 2008 r2

4. да. есть ли шанс, что вы могли бы переписать его с помощью вложенной замены? Спасибо!

5. я также заметил, что, помимо проблемы с заменой, возникают проблемы с = и >. есть идеи? Msg 102, Уровень 15, Состояние 1, Строка 15 Неверный синтаксис рядом с ‘=’. Msg 102, Уровень 15, Состояние 1, Строка 20 Неправильный синтаксис рядом с»>».