我需要返回所有文本结果(如果有的话),它们共享搜索字符串中常见的有限子字符串的最大长度。
在包含
的表格列中搜索“StackOverflow”"Stack",
"Sta",
"StackOv",
"StackOverthrow",
"StackOverSlow",
"StackFlow",
"Soverflow",
"StackOverCrow",
"StackOverSlow",
etc.
查询将返回“StackOverthrow”,因为它包含最大数量的匹配字符,以及独特结果集中的StackOverSlow和StackOverCrow。 目前我正在做一些效率低下的事情,首先是LIKE搜索第一个字符并继续重复和扩展搜索字符串直到找不到任何内容,并保持最后的好结果。
即。
select names from table where name like 'XX%';
"S" ->Results
"St"->Results
. .
"StackOver"->Results
"StackOverf"-> No results (Last result returning items beginning with StackOver etc as being the correct answer)
我知道这种方法效率极低,任何人都可以提供单一查询来实现这一结果吗?我知道我可以一次搜索所有组合并过滤代码中最长的结果,但是,我认为DB应该更好。
Edit1:注意上面的例子有点简化。 DB中的绝大多数数据在2到10个字符之间,最常见的匹配长度约为3个字符。表中有超过100K的记录。
Edit2:道歉,我需要澄清可能有多个正确的结果,并且结果可能包含需要删除的重复项。目前使用我的低效方法选择distinct很容易。
答案 0 :(得分:3)
使用name
上的索引,以下内容应该非常高效:
SELECT DISTINCT name
FROM myTable
WHERE name LIKE CASE
WHEN NOT EXISTS(SELECT * FROM myTable WHERE name LIKE 'S%') THEN '%'
WHEN NOT EXISTS(SELECT * FROM myTable WHERE name LIKE 'St%') THEN 'S%'
WHEN NOT EXISTS(SELECT * FROM myTable WHERE name LIKE 'Sta%') THEN 'St%'
WHEN NOT EXISTS(SELECT * FROM myTable WHERE name LIKE 'Stac%') THEN 'Sta%'
WHEN NOT EXISTS(SELECT * FROM myTable WHERE name LIKE 'Stack%') THEN 'Stac%'
WHEN NOT EXISTS(SELECT * FROM myTable WHERE name LIKE 'StackO%') THEN 'Stack%'
WHEN NOT EXISTS(SELECT * FROM myTable WHERE name LIKE 'StackOv%') THEN 'StackO%'
WHEN NOT EXISTS(SELECT * FROM myTable WHERE name LIKE 'StackOve%') THEN 'StackOv%'
WHEN NOT EXISTS(SELECT * FROM myTable WHERE name LIKE 'StackOver%') THEN 'StackOve%'
WHEN NOT EXISTS(SELECT * FROM myTable WHERE name LIKE 'StackOverf%') THEN 'StackOver%'
WHEN NOT EXISTS(SELECT * FROM myTable WHERE name LIKE 'StackOverfl%') THEN 'StackOverf%'
WHEN NOT EXISTS(SELECT * FROM myTable WHERE name LIKE 'StackOverflo%') THEN 'StackOverfl%'
WHEN NOT EXISTS(SELECT * FROM myTable WHERE name LIKE 'StackOverflow%') THEN 'StackOverflo%'
ELSE 'StackOverflow%'
END
在sqlfiddle上查看。
答案 1 :(得分:0)
不知道为什么你会先看看最小的。我会反过来做...首先尝试最长的精确匹配,如果没有找到,一次向后工作1个角色,直到找到一个。
答案 2 :(得分:0)
您可以在创建Levenshtein Distance存储函数后执行查询。这可以为您获得最佳匹配结果。
这不是我的代码。我是从here得到的。它似乎在sqlfiddle上测试得很好。
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;
您的查询可能如下所示:
SELECT names, levenshtein(`names`, 'StackOverflow') as dist
FROM mytable
ORDER BY dist;
以下是sqlfiddle上的内容。
结果看起来像这样,最低距离是最接近的匹配:
NAMES DIST
StackOverthrow 3
StackFlow 4
Soverflow 4
StackOv 6
Stack 8
Sta 10