在两个字符串之间匹配字符-TSQL

时间:2019-08-28 14:41:25

标签: tsql string-matching

我有两段代码将一个字符串拆分成单个字符,然后逐行返回。有人知道本质上可以使用分割字符串来确定它们是否彼此相似的任何内置函数吗?


SELECT SUBSTRING(Aux.Name, X.number+1, 1) AS Split 
FROM 
    (SELECT 'Wes Davids' as Name) AS Aux
    INNER JOIN master..spt_values X ON X.number < LEN(Aux.Name)
WHERE X.type = 'P'
 1 W
 2 e
 3 s
 4 
 5 D
 6 a
 7 v
 8 i
 9 d
10 s
SELECT SUBSTRING(Aux.Name, X.number+1, 1) AS Split 
FROM 
    (SELECT 'W Davids' as Name) AS Aux
    INNER JOIN master..spt_values X ON X.number < LEN(Aux.Name)
WHERE X.type = 'P'

 1 W
 2 
 3 D
 4 a
 5 v
 6 i
 7 d
 8 s

1 个答案:

答案 0 :(得分:1)

要将字符串拆分为N-Grams(在您的情况下为具体的字母组合),应使用ngrams8k。例如:

SELECT ng.* FROM dbo.ngrams8k('Wes Davids',1) AS ng;

返回:

position    token
----------- ---------
1           W
2           e
3           s
4            
5           D
6           a
7           v
8           i
9           d
10          s

例如,可以使用它来快速获取两个字符串之间最长的公共子字符串,如下所示。您可以通过将最长的公共子字符串(LCSS)的长度除以两个字符串中最长的字符串(L2)的长度来创建相似度评分:

DECLARE
  @string1 VARCHAR(100) = 'Joe Cook',
  @string2 VARCHAR(100) = 'J Cook';

SELECT TOP (1) *, LCSS = LEN(TRIM(ng.token)), similarity = 1.*LEN(TRIM(ng.token))/b.L2
FROM (VALUES(
  CASE WHEN LEN(@string1)<= LEN(@string2) THEN @string1      ELSE @string2 END,
  CASE WHEN LEN(@string1)<= LEN(@string2) THEN @string2      ELSE @string1 END,
  CASE WHEN LEN(@string1)<= LEN(@string2) THEN LEN(@string1) ELSE LEN(@string2)END,
  CASE WHEN LEN(@string1)<= LEN(@string2) THEN LEN(@string2) ELSE LEN(@string1)END
)) AS b(S1,S2,L1,L2)
CROSS JOIN  master..spt_values            AS x
CROSS APPLY dbo.ngrams8k(b.S1,x.number+1) AS ng
WHERE       x.[type] = 'P' 
AND         x.number < b.L1
AND         CHARINDEX(ng.token,b.S2) > 0
ORDER BY    LEN(TRIM(ng.token)) DESC
GO

返回:

S1             S2           position             token      LCSS  Similarity
-------------- ------------ -------------------- ---------- ----- ---------------------------------------
J Cook         Joe Cook     3                    Cook       4     0.50000000000

通过从两个字符串中较短的一个的长度(L1-Lev)中减去Levenshtein(lev)距离,然后将该值除以L2,可以获得更好的相似性评分: (L1-Lev)/ L2。您可以为此使用Phil Factor's Levenshtein Function

DECLARE
  @string1 VARCHAR(100) = 'James Cook',
  @string2 VARCHAR(100) = 'Jamess Cook';

SELECT 
  Lev        = dbo.LEVENSHTEIN(@string1,@string2), 
  Similarity = (1.*b.L1-dbo.LEVENSHTEIN(@string1,@string2))/b.L2
FROM (VALUES(
  CASE WHEN LEN(@string1)<= LEN(@string2) THEN @string1      ELSE @string2 END,
  CASE WHEN LEN(@string1)<= LEN(@string2) THEN @string2      ELSE @string1 END,
  CASE WHEN LEN(@string1)<= LEN(@string2) THEN LEN(@string1) ELSE LEN(@string2)END,
  CASE WHEN LEN(@string1)<= LEN(@string2) THEN LEN(@string2) ELSE LEN(@string1)END
)) AS b(S1,S2,L1,L2)
GO

返回:

Lev         Similarity
----------- ---------------------------------------
1           0.81818181818

这是如何使用Levenshtein距离测量相似性的示例。还有其他算法,例如Damerau–Levenshtein距离和The Longest Common Subsequence。 Damerau–Levenshtein的精确度更高,但速度较慢(Phil Factor在上述链接中具有Damerau–Levenshtein函数,并且在不同的帖子{{3}中具有[最长公共子序列函数]。相似性的公式相同{{ 1}}。最长公共子序列(LCSSq)比最长公共子串更准确(但速度较慢),但是使用相同的公式来计算相似性得分:(L1-DLev)/L2

希望这会帮助您入门。