【MySQL】Levenshtein编辑距离 自定义函数实现

Levenshtein Distance 编辑距离算法,也称字符串相似度算法。
计算从一个字符串,编辑成另一个字符串,需要的最少步骤。
例如“你好”和“你好啊”,需要加一个“啊”字,所以编辑距离为1。
该函数可以计算字段之间的相似度。

以下levenshtein为编辑距离函数,levenshtein_ratio为相似度(差距/字符串长度)百分比函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
--设置全局创建函数无条件信任
SET global log_bin_trust_function_creators=1;

--修改MySQL分隔符
delimiter $$

--创建levenshtein函数
CREATE FUNCTION levenshtein( s1 VARCHAR(255), s2 VARCHAR(255) )
RETURNS INT
deterministic
BEGIN
DECLARE s1_len, s2_len, i, j, c, c_temp, cost INT;
DECLARE s1_char CHAR;
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 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;
$$

--创建levenshtein_ratio函数
CREATE FUNCTION levenshtein_ratio( s1 VARCHAR(255), s2 VARCHAR(255) )
RETURNS INT
deterministic
BEGIN
DECLARE s1_len,s2_len,max_len INT;
SET s1_len = LENGTH(s1), s2_len = LENGTH(s2);
IF s1_len > s2_len THEN
SET max_len = s1_len;
ELSE
SET max_len = s2_len;
END IF;
RETURN round((1 - levenshtein(s1, s2) / max_len) * 100);
END;
$$

--还原MySQL分隔符
delimiter ;

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
--另一种实现
delimiter $$

CREATE FUNCTION similar_rate (s1 VARCHAR(255),s2 VARCHAR(255)) RETURNS FLOAT
BEGIN
DECLARE l1 INT DEFAULT 0;
DECLARE l2 INT DEFAULT 0;
DECLARE i INT DEFAULT 0;
DECLARE _ss VARCHAR(2) ;
DECLARE j INT DEFAULT 0;
DECLARE SUM INT DEFAULT 0;
SET l1=CHAR_LENGTH(s1);
SET l2=CHAR_LENGTH(s2);
IF l1=0 THEN RETURN 0;
END IF;
IF l2=0 THEN RETURN 0;
END IF;
SET i=0;
SET SUM=0;
myloop1:loop
SET i=i+1;
IF i>l1 THEN leave myloop1;
END IF;
SET _ss=substr(s1,i,1);
SET j=0;
myloop2:loop
SET j=j+1; IF j>l2 THEN
leave myloop2;
END IF;
IF _ss=substr(s2,j,1) THEN
SET SUM=SUM+1;
END IF;
END loop myloop2;
END loop myloop1;
RETURN SUM/l2;
END;

delimiter ;