You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SQL Server 2016相似字符串匹配求助:如何匹配近似名称?

针对你在SQL Server 2016里遇到的近似字符串匹配难题——尤其是像「Lizzards Pub - Various」和「Lizzards Pub, The - Various」这类带语序、标点差异的条目,Soundex和Difference函数确实不太够用,手动排查45000条数据更是不现实。下面是几个高效的解决方案,适合批量处理:

方案1:自定义Levenshtein编辑距离函数

Levenshtein距离是衡量两个字符串差异的经典指标(需要多少次增/删/改操作才能让两个字符串一致),SQL Server没有内置这个函数,但我们可以自己实现,然后设置相似度阈值来匹配近似项。

先创建这个函数:

CREATE FUNCTION dbo.LevenshteinDistance
(
    @s1 NVARCHAR(4000),
    @s2 NVARCHAR(4000)
)
RETURNS INT
AS
BEGIN
    DECLARE @len1 INT = LEN(@s1), @len2 INT = LEN(@s2);
    DECLARE @distanceTable TABLE (i INT, j INT, distance INT);

    IF @len1 = 0 RETURN @len2;
    IF @len2 = 0 RETURN @len1;

    INSERT INTO @distanceTable
    SELECT i, 0, i FROM (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) AS t(i) WHERE i <= @len1
    UNION ALL
    SELECT 0, j, j FROM (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) AS t(j) WHERE j <= @len2;

    DECLARE @i INT, @j INT, @cost INT;
    SET @i = 1;
    WHILE @i <= @len1
    BEGIN
        SET @j = 1;
        WHILE @j <= @len2
        BEGIN
            SET @cost = CASE WHEN SUBSTRING(@s1, @i, 1) = SUBSTRING(@s2, @j, 1) THEN 0 ELSE 1 END;

            UPDATE @distanceTable
            SET distance = 
                (SELECT MIN(d) FROM (VALUES
                    ((SELECT distance FROM @distanceTable WHERE i = @i - 1 AND j = @j) + 1),
                    ((SELECT distance FROM @distanceTable WHERE i = @i AND j = @j - 1) + 1),
                    ((SELECT distance FROM @distanceTable WHERE i = @i - 1 AND j = @j - 1) + @cost)
                ) AS t(d))
            WHERE i = @i AND j = @j;

            SET @j = @j + 1;
        END
        SET @i = @i + 1;
    END

    RETURN (SELECT distance FROM @distanceTable WHERE i = @len1 AND j = @len2);
END
GO

使用的时候,可以先过滤掉长度差异过大的条目(比如长度差超过3),再计算编辑距离,这样能大幅减少计算量:

SELECT 
    t1.Id AS Id1, t1.Name AS Name1,
    t2.Id AS Id2, t2.Name AS Name2,
    dbo.LevenshteinDistance(t1.Name, t2.Name) AS Distance
FROM YourTable t1
JOIN YourTable t2 ON t1.Id < t2.Id
WHERE ABS(LEN(t1.Name) - LEN(t2.Name)) <= 3
AND dbo.LevenshteinDistance(t1.Name, t2.Name) <= 3 -- 可根据实际情况调整阈值
ORDER BY Distance;
方案2:Damerau-Levenshtein距离(支持字符换位)

如果你的近似字符串经常出现字符换位或者语序小调整(比如"The"的位置变化),Damerau-Levenshtein距离更合适,它还支持字符的交换操作。同样自定义函数:

CREATE FUNCTION dbo.DamerauLevenshteinDistance
(
    @s1 NVARCHAR(4000),
    @s2 NVARCHAR(4000)
)
RETURNS INT
AS
BEGIN
    DECLARE @len1 INT = LEN(@s1), @len2 INT = LEN(@s2);
    DECLARE @d TABLE (i INT, j INT, dist INT);
    DECLARE @maxDist INT = @len1 + @len2;

    INSERT INTO @d VALUES (0, 0, @maxDist);
    INSERT INTO @d SELECT i, 0, i FROM (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) AS t(i) WHERE i <= @len1;
    INSERT INTO @d SELECT 0, j, j FROM (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) AS t(j) WHERE j <= @len2;

    DECLARE @i INT, @j INT, @cost INT, @lastI INT, @lastJ INT;
    DECLARE @char1 CHAR(1), @char2 CHAR(1);

    SET @i = 1;
    WHILE @i <= @len1
    BEGIN
        SET @lastI = 0;
        SET @char1 = SUBSTRING(@s1, @i, 1);
        SET @j = 1;
        WHILE @j <= @len2
        BEGIN
            SET @lastJ = 0;
            SET @char2 = SUBSTRING(@s2, @j, 1);
            SET @cost = CASE WHEN @char1 = @char2 THEN 0 ELSE 1 END;

            UPDATE @d
            SET dist = 
                (SELECT MIN(d) FROM (VALUES
                    ((SELECT dist FROM @d WHERE i = @i - 1 AND j = @j) + 1),
                    ((SELECT dist FROM @d WHERE i = @i AND j = @j - 1) + 1),
                    ((SELECT dist FROM @d WHERE i = @i - 1 AND j = @j - 1) + @cost)
                ) AS t(d))
            WHERE i = @i AND j = @j;

            IF @i > 1 AND @j > 1 AND @char1 = SUBSTRING(@s2, @j - 1, 1) AND @char2 = SUBSTRING(@s1, @i - 1, 1)
            BEGIN
                UPDATE @d
                SET dist = CASE WHEN dist > (SELECT dist FROM @d WHERE i = @lastI - 1 AND j = @lastJ - 1) + 1 THEN (SELECT dist FROM @d WHERE i = @lastI - 1 AND j = @lastJ - 1) + 1 ELSE dist END
                WHERE i = @i AND j = @j;
            END

            IF @cost = 0
            BEGIN
                SET @lastI = @i;
                SET @lastJ = @j;
            END

            SET @j = @j + 1;
        END
        SET @i = @i + 1;
    END

    RETURN (SELECT dist FROM @d WHERE i = @len1 AND j = @len2);
END
GO

用法和Levenshtein类似,阈值可以根据你的数据调整,比如设为2或3,能精准匹配语序小变动的条目。

方案3:先标准化字符串,再匹配

很多近似差异来自标点、语序、冗余词(比如"The"),先做标准化处理可以大幅降低匹配难度,比如:

  • 去掉所有非字母数字的字符(逗号、破折号等)
  • 把"The"这类前缀移到前面(比如把「Lizzards Pub, The」改成「The Lizzards Pub」)
  • 统一大小写
  • 去掉冗余的空格

先写一个标准化函数:

CREATE FUNCTION dbo.StandardizeString
(
    @input NVARCHAR(4000)
)
RETURNS NVARCHAR(4000)
AS
BEGIN
    -- 统一转小写
    SET @input = LOWER(@input);
    -- 去掉非字母数字的字符
    SET @input = REPLACE(REPLACE(REPLACE(@input, ',', ''), '-', ''), '.', '');
    -- 处理"The"在末尾的情况
    IF RIGHT(@input, 4) = ' the'
        SET @input = 'the ' + LEFT(@input, LEN(@input) - 4);
    -- 去掉多余空格
    WHILE CHARINDEX('  ', @input) > 0
        SET @input = REPLACE(@input, '  ', ' ');
    SET @input = LTRIM(RTRIM(@input));

    RETURN @input;
END
GO

标准化之后,再用编辑距离或者直接模糊匹配,效率会高很多:

SELECT 
    t1.Id AS Id1, t1.Name AS Name1,
    t2.Id AS Id2, t2.Name AS Name2
FROM YourTable t1
JOIN YourTable t2 ON t1.Id < t2.Id
WHERE dbo.StandardizeString(t1.Name) = dbo.StandardizeString(t2.Name)
OR dbo.LevenshteinDistance(dbo.StandardizeString(t1.Name), dbo.StandardizeString(t2.Name)) <= 2;
方案4:利用SQL Server全文索引

如果你的数据量持续增长,全文索引是更高效的选择。先给你的表创建全文索引:

-- 先创建全文目录
CREATE FULLTEXT CATALOG ftCatalog AS DEFAULT;
-- 创建全文索引(替换PK_YourTable为你的主键索引名)
CREATE FULLTEXT INDEX ON YourTable(Name)
KEY INDEX PK_YourTable
WITH STOPLIST = SYSTEM;

然后可以用CONTAINS或者FREETEXT来查询近似匹配,还可以自定义同义词库(比如把"Lizzards"和"Lizards"映射为同义词,或者把"The"设为停用词):

SELECT *
FROM YourTable t1
WHERE EXISTS (
    SELECT 1 FROM YourTable t2
    WHERE t1.Id != t2.Id
    AND CONTAINS(t2.Name, '"' + REPLACE(t1.Name, '-', ' ') + '"')
);

内容的提问来源于stack exchange,提问作者Aleksandar Picuric

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 07:14:43