我需要返回所有的文本结果(S),如果有的话,份额最大长度左界子串常见的搜索字符串。
鉴于“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:道歉,我需要澄清的是可能有不止一个正确的结果,并且该结果可以包含需要删除重复。 目前我的低效的方法选择不同的是容易的。
随着指数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个字符。
创建后,你可以做一个查询Levenshtein距离存储功能。 这可以得到最好的匹配结果你。
这不是我的代码。 从我得到这个位置 。 它似乎在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