获取匹配字符串的百分比
Get percentage of matching strings
我有两个字符串要匹配并获取匹配百分比。
鉴于:
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
+1 的问题。看来您正在尝试确定两个名称的相似程度。很难确定你是怎么做到的。例如,我对 Levenshtein Distance 非常熟悉,但不明白你是如何尝试使用它的。为了帮助您入门,我汇总了两种您可能会采用的方法。这不会是一个完整的答案,而是您将需要做的任何事情的工具。
要比较匹配 "name parts" 的数量,您可以像这样使用 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 = .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 = .9 或 90%。
同样,你所做的并不复杂,我只需要更详细的信息才能得到更好的答案。
一次又一次地对数百万行执行此操作将是一场噩梦...我将添加另一列(或 1:1
相关方 table)以永久存储 标准化字符串。试试这个:
--创建模型table并填充一些虚拟数据
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
保持这些值同步。
现在您可以对要与之比较的字符串使用相同的转换,并使用您以前的方法。由于重新排序,相同的片段将 return 100% 相似度。
我有两个字符串要匹配并获取匹配百分比。
鉴于:
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
+1 的问题。看来您正在尝试确定两个名称的相似程度。很难确定你是怎么做到的。例如,我对 Levenshtein Distance 非常熟悉,但不明白你是如何尝试使用它的。为了帮助您入门,我汇总了两种您可能会采用的方法。这不会是一个完整的答案,而是您将需要做的任何事情的工具。
要比较匹配 "name parts" 的数量,您可以像这样使用 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 = .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 = .9 或 90%。
同样,你所做的并不复杂,我只需要更详细的信息才能得到更好的答案。
一次又一次地对数百万行执行此操作将是一场噩梦...我将添加另一列(或 1:1
相关方 table)以永久存储 标准化字符串。试试这个:
--创建模型table并填充一些虚拟数据
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
保持这些值同步。
现在您可以对要与之比较的字符串使用相同的转换,并使用您以前的方法。由于重新排序,相同的片段将 return 100% 相似度。