MySQL SELECT查询:如何获取与搜索文本最相似的行
在MySQL中查询与搜索文本最相似的行
刚好碰到过类似的需求,给你整理几个实用方法,完美解决你说的搜'rakul'匹配'rahul'这类场景:
方法1:用Levenshtein距离(精准字符相似度匹配首选)
Levenshtein距离能算出两个字符串之间的编辑次数(插入、删除、替换字符),次数越少,字符串越相似。不过MySQL默认没内置这个函数,得先自定义一个:
DELIMITER // CREATE FUNCTION LEVENSHTEIN(str1 VARCHAR(255), str2 VARCHAR(255)) RETURNS INT DETERMINISTIC BEGIN DECLARE len1, len2, i, j, cost INT; DECLARE str1_char CHAR; DECLARE cv0, cv1 VARBINARY(256); SET len1 = CHAR_LENGTH(str1); SET len2 = CHAR_LENGTH(str2); SET cv0 = 0x00; SET i = 0; WHILE i <= len2 DO SET cv0 = CONCAT(cv0, UNHEX(HEX(i))); SET i = i + 1; END WHILE; SET i = 0; WHILE i < len1 DO SET str1_char = SUBSTRING(str1, i + 1, 1); SET cv1 = UNHEX(HEX(i + 1)); SET j = 0; WHILE j < len2 DO SET cost = IF(str1_char = SUBSTRING(str2, j + 1, 1), 0, 1); SET cv1 = CONCAT(cv1, UNHEX(HEX(LEAST( ORD(SUBSTRING(cv0, j + 2, 1)) + 1, ORD(SUBSTRING(cv1, j + 1, 1)) + 1, ORD(SUBSTRING(cv0, j + 1, 1)) + cost )))); SET j = j + 1; END WHILE; SET cv0 = cv1; SET i = i + 1; END WHILE; RETURN ORD(SUBSTRING(cv0, len2 + 1, 1)); END // DELIMITER ;
函数定义好后,就可以用它来查询了。假设你的表是users,要匹配的字段是name,搜索文本是'rakul',想取最相似的前10条:
SELECT name, LEVENSHTEIN(name, 'rakul') AS distance FROM users ORDER BY distance ASC LIMIT 10;
这里的distance就是编辑次数,像'rahul'和'rakul'的距离是1,会排在最前面,完全符合你的需求。
方法2:用SOUNDEX/SOUNDS LIKE(适合发音相似的场景)
如果你的需求是找发音相近的字符串,MySQL自带的工具就够用了。SOUNDEX会把字符串转换成发音编码,发音相似的字符串编码一致:
SELECT name FROM users WHERE SOUNDEX(name) = SOUNDEX('rakul');
或者用更简洁的SOUNDS LIKE语法:
SELECT name FROM users WHERE name SOUNDS LIKE 'rakul';
这个方法对发音接近的词效果很好,'rakul'和'rahul'发音差不多,肯定能匹配到。
方法3:全文搜索(适合长文本字段)
如果你的字段是较长的文本内容,还可以用MySQL的全文索引来做匹配。首先得给字段加个全文索引:
ALTER TABLE users ADD FULLTEXT INDEX ft_name (name);
然后用MATCH AGAINST查询匹配度:
SELECT name, MATCH(name) AGAINST('rakul' IN NATURAL LANGUAGE MODE) AS relevance FROM users ORDER BY relevance DESC;
这里的relevance是匹配得分,得分越高越相似。不过这个方法对短字符串的匹配效果不如前两种,更适合大段文本的场景。
内容的提问来源于stack exchange,提问作者sahil sanwal




