#sql #sql-server #tsql
Вопрос:
Мне было интересно, может ли кто-нибудь здесь придумать для меня какое-нибудь решение, которое заняло бы 2 строки и сравнило бы их слово за словом, чтобы дать мне процентное совпадение всей строки.
Пример: Если бы я хотел сравнить эти 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 слов.
Комментарии:
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 Неправильный синтаксис рядом с»>».