T-SQL:字符匹配平均占比计算及名称相似性匹配实现需求
嘿,针对你在SQL Server 2016里处理两个关联表名称相似匹配的需求,我整理了几个实用的解决方案,这些方法能比单纯的LIKE更好地应对那些微小差异的场景:
这两个函数是SQL Server内置的,专门用来判断字符串发音的相似度,适合处理像“Smith”和“Smyth”这种拼写不同但发音接近的情况。
SELECT t1.[Key], t1.Name AS Name_Table1, t2.Name AS Name_Table2, CASE WHEN DIFFERENCE(t1.Name, t2.Name) >= 3 THEN '匹配' ELSE '不匹配' END AS IsNameMatch FROM Table1 t1 JOIN Table2 t2 ON t1.[Key] = t2.[Key];
小贴士:DIFFERENCE返回0-4的数值,4代表发音完全一致,你可以根据业务需求调整阈值(比如>=3就算匹配)。不过这个方法对非英文名称的支持不太友好。
编辑距离指的是把一个字符串转换成另一个字符串所需的最少编辑操作(插入、删除、替换)次数,能精准处理拼写小差异,比如“Microsoft”和“Microsft”。SQL Server没有内置这个函数,我们可以自己写一个:
-- 先创建Levenshtein距离函数 CREATE FUNCTION dbo.LevenshteinDistance(@s1 NVARCHAR(MAX), @s2 NVARCHAR(MAX)) RETURNS INT AS BEGIN DECLARE @len1 INT = LEN(@s1), @len2 INT = LEN(@s2); DECLARE @distance TABLE(i INT, j INT, val INT); INSERT INTO @distance VALUES(0,0,0); IF @len1 = 0 RETURN @len2; IF @len2 = 0 RETURN @len1; DECLARE @i INT, @j INT; SET @i = 1; WHILE @i <= @len1 BEGIN INSERT INTO @distance VALUES(@i, 0, @i); SET @i = @i + 1; END SET @j = 1; WHILE @j <= @len2 BEGIN INSERT INTO @distance VALUES(0, @j, @j); SET @j = @j + 1; END SET @i = 1; WHILE @i <= @len1 BEGIN SET @j = 1; WHILE @j <= @len2 BEGIN DECLARE @cost INT = CASE WHEN SUBSTRING(@s1, @i, 1) = SUBSTRING(@s2, @j, 1) THEN 0 ELSE 1 END; DECLARE @minVal INT = (SELECT MIN(val) FROM (VALUES((SELECT val FROM @distance WHERE i = @i-1 AND j = @j) + 1), ((SELECT val FROM @distance WHERE i = @i AND j = @j-1) + 1), ((SELECT val FROM @distance WHERE i = @i-1 AND j = @j-1) + @cost)) AS temp(v)); INSERT INTO @distance VALUES(@i, @j, @minVal); SET @j = @j + 1; END SET @i = @i + 1; END RETURN (SELECT val FROM @distance WHERE i = @len1 AND j = @len2); END GO -- 使用函数判断匹配(这里设置距离<=2视为匹配,可按需调整) SELECT t1.[Key], t1.Name AS Name_Table1, t2.Name AS Name_Table2, CASE WHEN dbo.LevenshteinDistance(t1.Name, t2.Name) <= 2 THEN '匹配' ELSE '不匹配' END AS IsNameMatch FROM Table1 t1 JOIN Table2 t2 ON t1.[Key] = t2.[Key];
小贴士:这个自定义函数在处理大数据量时性能可能会打折扣,建议先通过WHERE条件过滤掉完全不相关的记录,再计算相似度。
如果你的SQL Server已经开启了全文搜索功能,这个方法能处理更复杂的场景,比如同义词、词干匹配(比如“running”和“run”)。
首先要给两个表的Name字段创建全文索引,然后就可以用下面的查询:
SELECT t1.[Key], t1.Name AS Name_Table1, t2.Name AS Name_Table2, CASE WHEN FREETEXT(t2.Name, t1.Name) THEN '匹配' ELSE '不匹配' END AS IsNameMatch FROM Table1 t1 JOIN Table2 t2 ON t1.[Key] = t2.[Key];
小贴士:全文搜索需要提前配置,适合处理自然语言类的名称相似性,但对简单拼写错误的处理不如编辑距离直接。
很多时候名称不匹配是因为格式、缩写或特殊字符导致的,比如“ABC Corp.”和“abc corporation”。我们可以先做标准化处理,再判断匹配:
CREATE FUNCTION dbo.StandardizeName(@name NVARCHAR(MAX)) RETURNS NVARCHAR(MAX) AS BEGIN -- 统一转小写 SET @name = LOWER(@name); -- 移除所有非字母数字的字符 WHILE PATINDEX('%[^a-z0-9]%', @name) > 0 SET @name = STUFF(@name, PATINDEX('%[^a-z0-9]%', @name), 1, ''); -- 替换常见缩写(可根据业务需求添加更多规则) SET @name = REPLACE(@name, 'corp', 'corporation'); SET @name = REPLACE(@name, 'inc', 'incorporated'); SET @name = REPLACE(@name, 'ltd', 'limited'); RETURN @name; END GO -- 使用标准化后的名称判断匹配 SELECT t1.[Key], t1.Name AS Name_Table1, t2.Name AS Name_Table2, CASE WHEN dbo.StandardizeName(t1.Name) = dbo.StandardizeName(t2.Name) THEN '匹配' ELSE '不匹配' END AS IsNameMatch FROM Table1 t1 JOIN Table2 t2 ON t1.[Key] = t2.[Key];
小贴士:这个方法可以和前面的任意一种方法结合使用,先标准化再计算相似度,能大幅提升匹配准确率。
内容的提问来源于stack exchange,提问作者SUMguy

