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

如何在MySQL查询中实现模糊分组?该功能是否可行?

在MySQL中实现非英文数据的模糊分组(匹配不完全重复记录)

我完全懂你的困扰——非英文数据用不了SOUNDEX(),SSIS又完全没接触过,想直接在MySQL里搞定模糊分组找重复记录,确实得找适配非英文场景的方案。下面给你几个实用的思路和具体实现:

1. 利用编辑距离(Levenshtein Distance)分组

编辑距离能衡量两个字符串的相似程度,数值越小代表越相似。MySQL本身没有内置这个函数,但你可以自定义一个:

DELIMITER $$
CREATE FUNCTION levenshtein(s1 VARCHAR(255), s2 VARCHAR(255)) 
RETURNS INT
DETERMINISTIC
BEGIN
    DECLARE s1_len, s2_len, i, j, c, c_temp INT;
    DECLARE s1_char CHAR;
    DECLARE cv0, cv1 VARBINARY(256);
    
    SET s1_len = CHAR_LENGTH(s1), s2_len = CHAR_LENGTH(s2);
    SET cv0 = 0x00;
    FOR i FROM 1 TO s2_len DO
        SET cv0 = CONCAT(cv0, UNHEX(HEX(i)));
    END FOR;
    
    FOR i FROM 1 TO s1_len DO
        SET s1_char = SUBSTRING(s1, i, 1);
        SET cv1 = UNHEX(HEX(i));
        SET j = 1;
        WHILE j <= s2_len DO
            SET c = IF(s1_char = SUBSTRING(s2, j, 1), 0, 1);
            SET c_temp = CONV(HEX(SUBSTRING(cv0, j, 1)), 16, 10) + c;
            SET c_temp = LEAST(
                CONV(HEX(SUBSTRING(cv1, j, 1)), 16, 10) + 1,
                c_temp
            );
            SET c_temp = LEAST(
                CONV(HEX(SUBSTRING(cv0, j+1, 1)), 16, 10) + 1,
                c_temp
            );
            SET cv1 = CONCAT(cv1, UNHEX(HEX(c_temp)));
            SET j = j + 1;
        END WHILE;
        SET cv0 = cv1;
    END FOR;
    
    RETURN CONV(HEX(SUBSTRING(cv0, s2_len+1, 1)), 16, 10);
END$$
DELIMITER ;

之后你可以用这个函数分组相似记录,比如把编辑距离≤2的归为一组(阈值可以根据你的数据灵活调整):

SELECT 
    t1.id,
    t1.content,
    GROUP_CONCAT(t2.id SEPARATOR ',') AS similar_record_ids,
    GROUP_CONCAT(t2.content SEPARATOR '; ') AS similar_contents
FROM your_table t1
JOIN your_table t2 ON t1.id < t2.id 
    AND levenshtein(t1.content, t2.content) <= 2
GROUP BY t1.id, t1.content
HAVING COUNT(t2.id) > 0;

2. 基于n-gram分词的模糊匹配分组

对于中文、日文这类非英文数据,n-gram(比如二元分词)是更有效的相似性判断方式。你可以先把字符串拆分成n-gram集合,再计算交集比例:

先自定义一个生成n-gram的函数(以二元分词为例):

DELIMITER $$
CREATE FUNCTION generate_ngrams(s VARCHAR(255), n INT)
RETURNS TEXT
DETERMINISTIC
BEGIN
    DECLARE result TEXT DEFAULT '';
    DECLARE len INT;
    DECLARE i INT DEFAULT 1;
    
    SET len = CHAR_LENGTH(s);
    IF len < n THEN RETURN s; END IF;
    
    WHILE i <= len - n + 1 DO
        SET result = CONCAT(result, SUBSTRING(s, i, n), ',');
        SET i = i + 1;
    END WHILE;
    RETURN TRIM(TRAILING ',' FROM result);
END$$
DELIMITER ;

然后用这个函数计算两个字符串的n-gram相似度,进而分组:

SELECT 
    t1.id,
    t1.content,
    GROUP_CONCAT(t2.id) AS similar_ids
FROM your_table t1
JOIN your_table t2 ON t1.id != t2.id
WHERE 
    (SELECT COUNT(*) FROM 
        (SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(generate_ngrams(t1.content,2), ',', numbers.n), ',', -1) AS gram
         FROM (SELECT 1 n UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6) numbers
         WHERE n <= LENGTH(generate_ngrams(t1.content,2)) - LENGTH(REPLACE(generate_ngrams(t1.content,2), ',', '')) + 1) g1
        JOIN 
        (SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(generate_ngrams(t2.content,2), ',', numbers.n), ',', -1) AS gram
         FROM (SELECT 1 n UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6) numbers
         WHERE n <= LENGTH(generate_ngrams(t2.content,2)) - LENGTH(REPLACE(generate_ngrams(t2.content,2), ',', '')) + 1) g2
        ON g1.gram = g2.gram) / 
    (SELECT COUNT(*) FROM 
        (SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(generate_ngrams(t1.content,2), ',', numbers.n), ',', -1) AS gram
         FROM (SELECT 1 n UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6) numbers
         WHERE n <= LENGTH(generate_ngrams(t1.content,2)) - LENGTH(REPLACE(generate_ngrams(t1.content,2), ',', '')) + 1) g1
        UNION
        (SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(generate_ngrams(t2.content,2), ',', numbers.n), ',', -1) AS gram
         FROM (SELECT 1 n UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6) numbers
         WHERE n <= LENGTH(generate_ngrams(t2.content,2)) - LENGTH(REPLACE(generate_ngrams(t2.content,2), ',', '')) + 1) g2) >= 0.6 -- 相似度阈值,可按需调整
GROUP BY t1.id, t1.content;

3. 简单前缀匹配分组(适合有重复前缀的场景)

如果你的重复数据大多是前缀相似,比如"北京市朝阳区XX街道"和"北京市朝阳区YY街道",可以直接截取前缀分组:

SELECT 
    SUBSTRING(content, 1, 8) AS prefix, -- 截取前8个字符作为分组依据,长度可调整
    GROUP_CONCAT(id) AS record_ids,
    GROUP_CONCAT(content) AS all_contents
FROM your_table
GROUP BY prefix
HAVING COUNT(id) > 1;

注意事项

  • 性能优化:如果数据量很大,编辑距离和n-gram的计算会比较耗时,建议先过滤掉明显不相关的记录(比如长度差异超过一定范围的),或者给字段加合适的索引。
  • 阈值调整:每个场景的相似性标准不同,你需要根据实际数据调整编辑距离阈值、n-gram相似度比例或者前缀长度。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 08:59:30