获取匹配字符串的百分比

时间:2019-02-13 12:22:42

标签: sql-server tsql sql-server-2008-r2

我有两个要匹配的字符串并获得匹配百分比。

给出:

String 1: John Smith Makde

String 2: Makde John Smith   

使用了以下用户定义的标量函数。

CREATE FUNCTION [dbo].[udf_GetPercentageOfTwoStringMatching]
(
    @string1 NVARCHAR(1000)
    ,@string2 NVARCHAR(1000)
)
RETURNS INT

--WITH ENCRYPTION 
AS
BEGIN

    DECLARE @levenShteinNumber INT

    DECLARE @string1Length INT = LEN(@string1), @string2Length INT = LEN(@string2)
    DECLARE @maxLengthNumber INT = CASE WHEN @string1Length > @string2Length THEN @string1Length ELSE @string2Length END

    SELECT @levenShteinNumber = [dbo].[f_ALGORITHM_LEVENSHTEIN] (@string1  ,@string2)

    DECLARE @percentageOfBadCharacters INT = @levenShteinNumber * 100 / @maxLengthNumber

    DECLARE @percentageOfGoodCharacters INT = 100 - @percentageOfBadCharacters

    -- Return the result of the function
    RETURN @percentageOfGoodCharacters

END

函数调用:

SELECT dbo.f_GetPercentageOfTwoStringMatching('John Smith Makde','Makde John Smith')    

输出:

7

但是当我将两个字符串都放在相同的位置时

SELECT dbo.f_GetPercentageOfTwoStringMatching('John Smith Makde','John Smith Makde')

输出:

100

预期结果:由于两个字符串单词相同,但顺序不同,因此我希望100%匹配百分比。

100

2 个答案:

答案 0 :(得分:2)

问题的

+1。您似乎正在尝试确定两个名称的相似程度。很难确定您的操作方式。例如,我对Levenshtein距离非常熟悉,但不了解您如何尝试使用它。为了让您入门,我整理了两种解决方法。这不是一个完整的答案,而是您将需要做的所有工作所需的工具。

要比较匹配的“名称部分”的数量,可以使用DelimitedSplit8K,如下所示:

DECLARE 
  @String1 VARCHAR(100) = 'John Smith Makde Sr.',
  @String2 VARCHAR(100) = 'Makde John Smith Jr.';

SELECT COUNT(*)/(1.*LEN(@String1)-LEN(REPLACE(@string1,' ',''))+1)
FROM
(
  SELECT      s1.item
  FROM        dbo.delimitedSplit8K(@String1,' ') AS s1
  INTERSECT
  SELECT      s2.item
  FROM        dbo.delimitedSplit8K(@String2,' ') AS s2
) AS a

在这里,我将名称拆分为原子值并计算匹配的原子值。然后,我们将该数字除以值的数量。 3/4 = 0.75,表示75%;四个名称中的3个匹配。

另一种方法是像这样使用NGrams8K

DECLARE 
  @String1 VARCHAR(100) = 'John Smith Makde Sr.',
  @String2 VARCHAR(100) = 'Makde John Smith Jr.';

SELECT (1.*f.L-f.MM)/f.L
FROM
(
  SELECT 
    MM = SUM(ABS(s1.C-s2.C)), 
    L  = CASE WHEN LEN(@String1)>LEN(@string2) THEN LEN(@String1) ELSE LEN(@string2) END
  FROM
  (
    SELECT s1.token, COUNT(*)
    FROM   samd.NGrams8k(@String1,1) AS s1
    GROUP BY s1.token
  ) AS s1(T,C)
  JOIN 
  (
    SELECT s1.token, COUNT(*)
    FROM   samd.NGrams8k(@String2,1) AS s1
    GROUP BY s1.token
  ) AS s2(T,C)
  ON  s1.T=s2.T  -- Letters that are equal
  AND s1.C<>s2.C -- ... but the QTY is different
) AS f;

在这里,我们要计算字符并减去不匹配项。有两个(一个额外的J和一个额外的S)。两个字符串中的较长者为20,则有18个字符,其中字母和数量相等。 18/20 = 0.9或90%。

同样,您所做的并不复杂,我只需要更多详细信息即可获得更好的答案。

答案 1 :(得分:1)

一次又一次地执行数百万行将是一场噩梦……我将添加另一列(或与1:1相关的边表)以永久存储归一化字符串。试试这个:

-创建一个模型表并用一些虚拟数据填充它

CREATE TABLE #MockUpYourTable(ID INT IDENTITY, SomeName VARCHAR(1000));
INSERT INTO #MockUpYourTable VALUES('Makde John Smith')
                                  ,('Smith John Makde')
                                  ,('Some other string')
                                  ,('string with with duplicates with');
GO

-添加一列以存储规范化的字符串

ALTER TABLE #MockupYourTable ADD NormalizedName VARCHAR(1000);
GO

-使用此脚本将您的字符串分成多个片段,并将它们重新连接为规范排序的无重复字符串。

UPDATE #MockUpYourTable SET NormalizedName=CAST('<x>' + REPLACE((SELECT LOWER(SomeName) AS [*] FOR XML PATH('')),' ','</x><x>') + '</x>' AS XML)
                                            .query(N'
                                                    for $fragment in distinct-values(/x/text())
                                                    order by $fragment
                                                    return $fragment
                                                    ').value('.','nvarchar(1000)');           
GO

-检查结果

SELECT * FROM #MockUpYourTable

ID  SomeName                            NormalizedName
----------------------------------------------------------
1   Makde John Smith                    john makde smith
2   Smith John Makde                    john makde smith
3   Some other string                   other some string
4   string with with duplicates with    duplicates string with

--Clean-Up
GO
DROP TABLE #MockUpYourTable

提示。使用触发器ON INSERT, UPDATE使这些值保持同步。

现在,您可以对要与之进行比较的字符串使用相同的转换,并使用以前的方法。由于重新排序,相同的片段将返回100%的相似性。