2016-08-03 61 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

相關問題