2016-08-03 68 views
1

是否有可能在MySQL中从表中选择与给定字符串只有一个字符差异的所有字符串?MySQL:如何有效地选择只有一个字符差异的字符串?

例如。 Iphone 5Iphone 5sModel x-1Models x1

+0

这个字符的差异必须在字符串的末尾吗? – Mureinik

+0

没有。可以在字符串的任何位置 – Francesco

+0

这取决于,你的意思是1个字符的区别。检查[这个答案](http://stackoverflow.com/questions/3338889/how-to-find-similar-results-and-sort-by-similarity)。 – Nosyara

回答

1

如果你可以添加一个用户函数到MySQL,那么你可以使用莱文斯坦的距离。代码见this other question。例如,您可以查询WHERE LEVENSHTEIN(description, 'iphone 5') <= 2。你会发现“iPhone 5S”,也可能是“ipohne 5”,这可能是一个加号。

否则,具体个案很容易(例如,REGEX 'iphone.*'或类似),但一般情况下将是一个噩梦实施。

表演

该版本被修改为接受第三个参数MAXCOST。当达到levenshtein子循环的最大成本时,搜索功能将简单地中止。排除病理情况,并记住实际成本可能稍微偏离(即排除12的成本可能包括成本为12的字符串,排除成本为11的字符串),这会减少其中Levenshtein距离不合理。这些案件可能对你没有兴趣。

所以如果你想要距离小于3的字符串,你可以使用WHERE levenshtein(string1, string2, 4) < 3。距离3-4或以上的所有字符串现在将立即返回4 ,并被排除。

测试:

mysql> select BENCHMARK(1000,levenshtein('PIPPORIDICOLO','LUKASPERICOLO', 99)); 
+------------------------------------------------------------------+ 
| BENCHMARK(1000,levenshtein('PIPPORIDICOLO','LUKASPERICOLO', 99)) | 
+------------------------------------------------------------------+ 
|                0 | 
+------------------------------------------------------------------+ 
1 row in set (2.50 sec) 

mysql> select BENCHMARK(1000,levenshtein('PIPPORIDICOLO','LUKASPERICOLO', 3)); 
+-----------------------------------------------------------------+ 
| BENCHMARK(1000,levenshtein('PIPPORIDICOLO','LUKASPERICOLO', 3)) | 
+-----------------------------------------------------------------+ 
|                0 | 
+-----------------------------------------------------------------+ 
1 row in set (0.08 sec) 

这里的成本为2500减少到80毫秒每1000次评估。

这是修改后的代码。在定义这个新的函数之前,您必须先删除旧的函数,除非您更改名称。

DELIMITER $$ 
CREATE FUNCTION levenshtein(s1 VARCHAR(255), s2 VARCHAR(255), maxcost INTEGER) 
RETURNS INT 
DETERMINISTIC 
BEGIN 
DECLARE s1_len, s2_len, i, j, c, c_temp, cost INT; 
DECLARE s1_char CHAR; 
-- max strlen=255 
DECLARE cv0, cv1 VARBINARY(256); 
SET s1_len = CHAR_LENGTH(s1), s2_len = CHAR_LENGTH(s2), cv1 = 0x00, j = 1, i = 1, c = 0; 
IF s1 = s2 THEN 
    RETURN 0; 
ELSEIF s1_len = 0 THEN 
    RETURN s2_len; 
ELSEIF s2_len = 0 THEN 
    RETURN s1_len; 
ELSE 
    WHILE j <= s2_len DO 
     SET cv1 = CONCAT(cv1, UNHEX(HEX(j))), j = j + 1; 
    END WHILE; 
    WHILE i <= s1_len DO 
     SET s1_char = SUBSTRING(s1, i, 1), c = i, cv0 = UNHEX(HEX(i)), j = 1; 
     WHILE j <= s2_len DO 
      SET c = c + 1; 
      IF c > maxcost THEN 
       RETURN maxcost; 
      END IF; 
      IF s1_char = SUBSTRING(s2, j, 1) THEN 
       SET cost = 0; 
      ELSE 
       SET cost = 1; 
      END IF; 
      SET c_temp = CONV(HEX(SUBSTRING(cv1, j, 1)), 16, 10) + cost; 
      IF c > c_temp THEN SET c = c_temp; END IF; 
      SET c_temp = CONV(HEX(SUBSTRING(cv1, j+1, 1)), 16, 10) + 1; 
      IF c > c_temp THEN SET c = c_temp; END IF; 
      SET cv0 = CONCAT(cv0, UNHEX(HEX(c))), j = j + 1; 
     END WHILE; 
     SET cv1 = cv0, i = i + 1; 
    END WHILE; 
END IF; 
RETURN c; 
END$$ 
DELIMITER ; 
+0

谢谢,我会读 – Francesco

+0

我确实添加了描述的功能,但性能呢?如果我有一张20,000行的桌子怎么办? – Francesco

+0

我试过,它运作良好,但我认为在我的MySQL需要大约5分钟给我结果:( – Francesco

相关问题