我基本上有一个两列表,其中包含主键和公司名称,大约有20,000行。
我的任务是找到所有重复的条目。
我最初尝试使用soundex,但它会匹配完全不同的公司,只因为他们有相似的第一句话。所以这引导我进入levenshtein距离算法。
问题是,查询需要无限期的时间。我现在已经离开了大约10个小时,但仍然没有回应。
以下是查询:
SELECT *
FROM `Companies` a, `Companies` b
WHERE levenshtein(a.name, b.name)<5
AND a.id<>b.id
这是我正在使用的levenshtein函数(从this post获得)
DELIMITER $$
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;
-- 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 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 :(得分:4)
我知道至少有一项可能会缩短运行时间的优化:
AND a.id < b.id
当你已经测试了a = 2,b = 1时,这可以防止你测试a = 1,b = 2.
但它仍然会是O(n ^ 2),但我看不出你怎么能做那么多。
答案 1 :(得分:3)
重复条目的名称有多相似?
如果它们准确无误,您可以按名称分组:
SELECT REPLACE(name, ' ', '') as name, count(id) as totalDuplicates
FROM `Companies`
GROUP BY REPLACE(name, ' ', '')
计数大于1的任何内容都是重复的
答案 2 :(得分:2)
所以我在这个帖子中实现了一堆建议,以减少我的查询时间。
我将名称列为索引,更改了a.id&lt;&gt; b.id到a.id&lt; b.id减少已经比较行的重新比较,并添加LEFT(a.name,3)= LEFT(b.name,3)以防止在前3个字符可以轻易排除的行上执行重型levenshtein函数。 / p>
这是我使用的查询:
SELECT *
FROM `Companies` a, `Companies` b
WHERE LEFT(a.name, 3) = LEFT(b.name, 3)
AND a.id < b.id
AND levenshtein(a.name, b.name)<3
这花了大约2个小时才完成,并给了我964个结果。之后,我将结果导出为.csv并将其导入另一个表,表2。 表2的结构如下:
COL 1, COL 2, COL 3, COL 4
a.id, a.name, b.id, b.name
我注意到表2中有很多结果实际上是不同的公司,但只有几个字符,使得levinshtein距离在排序时无效。例如:&#34; Body FX&#34;,&#34; Body Fit&#34;,或&#34; Baxco&#34;,&#34; Baxyl&#34;。
我试图通过在字符串的最后2个字符上比较RIGHT()来过滤掉更多的名字,但是因为一些名字是复数而遇到了问题,例如&#34; Aroostock Medical Center&#34;和#34; Aroostock医疗中心&#34;。所以我编写了自己的RIGHT_PLURAL()函数,忽略了复数字符。
DROP FUNCTION IF EXISTS RIGHT_PLURAL;
DELIMITER $$
CREATE FUNCTION RIGHT_PLURAL(input VARCHAR(50), right_input INT)
RETURNS VARCHAR(50)
BEGIN
DECLARE length INT;
SET length = LENGTH(input);
IF RIGHT(input, 2)="'s" THEN
RETURN SUBSTR(input, length-right_input-1, right_input);
ELSEIF RIGHT(input, 1)="s" THEN
RETURN SUBSTR(input, length-right_input, right_input);
ELSE
RETURN RIGHT(input, right_input);
END IF;
END;
$$
DELIMITER ;
我跑了
SELECT *
FROM `TABLE 2`
WHERE RIGHT_PLURAL(
`COL 2` , 2
) = RIGHT_PLURAL(
`COL 4` , 2
)
并且重复893个。我很满意。我将结果集复制到表3,然后执行以下操作。
DELETE
FROM `Companies`
WHERE `id` IN ( SELECT `COL 1` FROM `TABLE 3` )
我的数据库现在大部分都是免费的!剩下的只有少数流浪者是由于严重错误拼写的名字。
答案 3 :(得分:1)