MySQL中如何实现近似字符的模糊匹配查询?
首先先还原你的测试场景,你的示例表结构和数据如下:
create table `products` ( `Id` int (11), `Name` varchar (900) ); insert into `products` (`Id`, `Name`) values(1,'LORFAST TAB.'); insert into `products` (`Id`, `Name`) values(1,'SPORIDEX REDIMIX DROP'); insert into `products` (`Id`, `Name`) values(1,'MICROGEST 400MG'); insert into `products` (`Id`, `Name`) values(1,'ANTIPLAR PLUS TAB'); insert into `products` (`Id`, `Name`) values(1,'DECA DURABOLIN 100MG');
你遇到的问题很典型:普通的LIKE '%关键词%'只能做精确的子串匹配,当用户输入有拼写错误(比如漏写了LORFAST里的A变成LORFST)时,就无法返回近似匹配的结果。下面给你几种实用的解决方案,针对你的场景逐一说明:
方法1:使用编辑距离函数(Levenshtein Distance)
编辑距离是衡量两个字符串差异的核心指标,它代表把一个字符串转换成另一个字符串所需的最少单字符编辑操作(插入、删除、替换)次数。你的例子中,LORFST和LORFAST的编辑距离是1(只需要插入一个A),所以我们可以设定一个合理的阈值(比如1或2,根据业务需求调整),筛选出编辑距离小于等于阈值的记录。
示例查询
如果你的MySQL环境支持LEVENSHTEIN()函数(比如MariaDB默认自带,部分MySQL版本需要安装lib_mysqludf_str插件),直接用这条语句就能得到你想要的结果:
SELECT * FROM products WHERE LEVENSHTEIN(Name, 'LORFST') <= 1;
这条语句会返回所有Name字段和LORFST的编辑距离不超过1的记录,正好匹配到LORFAST TAB.。
自定义编辑距离函数(如果没有内置函数)
如果你的MySQL没有自带LEVENSHTEIN(),可以自己创建一个自定义函数来实现这个逻辑:
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); IF s1_len = 0 THEN RETURN s2_len; END IF; IF s2_len = 0 THEN RETURN s1_len; END IF; 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 cv1 = CONCAT(cv1, UNHEX(HEX(LEAST( CONV(HEX(SUBSTRING(cv1, j, 1)), 16, 10) + 1, c_temp, CONV(HEX(SUBSTRING(cv0, j+1, 1)), 16, 10) + 1 )))); SET j = j + 1; END WHILE; SET cv0 = cv1; END FOR; RETURN CONV(HEX(SUBSTRING(cv0, s2_len, 1)), 16, 10); END // DELIMITER ;
创建完成后,就可以使用上面的查询语句了。
方法2:使用正则表达式快速匹配(适合简单漏字场景)
如果只是针对漏写单个字符的情况,可以用正则表达式来允许字符之间存在任意单个字符(包括没有)。把输入的LORFST转换成L.?O.?R.?F.?S.?T,这样就能匹配中间多一个字符的目标字符串:
SELECT * FROM products WHERE Name REGEXP 'L.?O.?R.?F.?S.?T';
这个方法简单直接,但局限性比较大——只能处理漏写或多写单个字符的情况,无法应对字符替换类的拼写错误。
方法3:全文搜索(适合大数据量场景)
如果你的数据集很大,上面的方法因为需要全表扫描,性能会明显下降。这时可以考虑使用MySQL的全文索引来优化:
- 先给
Name字段创建全文索引:
ALTER TABLE products ADD FULLTEXT INDEX ft_name(Name);
- 然后使用
MATCH ... AGAINST进行模糊查询,结合布尔模式或查询扩展来获取近似结果:
SELECT * FROM products WHERE MATCH(Name) AGAINST('LORFST' IN BOOLEAN MODE);
不过全文搜索对拼写错误的支持需要根据MySQL版本和配置调整,比如启用ngram分词(适合短词或非英文场景),或者配合第三方拼写纠错工具来提升效果。
注意事项
- 编辑距离和正则表达式的方法都会触发全表扫描,数据量大时性能不佳,建议仅在小数据集或测试环境使用。
- 编辑距离的阈值需要根据实际业务调整:阈值设得越高,能匹配的拼写错误越多,但也可能返回更多无关结果。
内容的提问来源于stack exchange,提问作者Juned Ansari

