MySQL选择具有差异容忍度的不同
在我的数据库中,我有许多非常相似但不完全相同的条目。例如,只有两个字符可能不同,例如: 罗1:天气很好,看到了吗 罗2:天气很好,看到了吗 我想去掉这些部分重复项,只接收这两行的一个结果。不管是哪一个,我建议使用出现的第一个 我可以利用MySQL中的任何功能来高效地完成这项工作吗?我的第一个想法是提取更多数据并对字符串进行比较,如果匹配字符超过某个阈值,而不是忽略它。缺点是,我永远不知道我必须从数据库中提取多少条目,而且效率也很低,因为我必须将每一行与表中的所有其他行进行比较 更新: 更具体地说,在用例中:方差的位置并不总是在字符串的末尾,它也可能不止两个字符会发生变化。字符串长度因每行而异。您可以使用SOUNDEXMySQL选择具有差异容忍度的不同,mysql,distinct,Mysql,Distinct,在我的数据库中,我有许多非常相似但不完全相同的条目。例如,只有两个字符可能不同,例如: 罗1:天气很好,看到了吗 罗2:天气很好,看到了吗 我想去掉这些部分重复项,只接收这两行的一个结果。不管是哪一个,我建议使用出现的第一个 我可以利用MySQL中的任何功能来高效地完成这项工作吗?我的第一个想法是提取更多数据并对字符串进行比较,如果匹配字符超过某个阈值,而不是忽略它。缺点是,我永远不知道我必须从数据库中提取多少条目,而且效率也很低,因为我必须将每一行与表中的所有其他行进行比较 更新: 更具体地说
SOUNDEX(str)
Returns a soundex string from str. Two strings that sound almost the same should have identical soundex strings. A standard soundex string is four characters long, but the SOUNDEX() function returns an arbitrarily long string. You can use SUBSTRING() on the result to get a standard soundex string. All non-alphabetic characters in str are ignored. All international alphabetic characters outside the A-Z range are treated as vowels.
mysql> SELECT SOUNDEX('Hello');
+---------------------------------------------------------+
| SOUNDEX('Hello') |
+---------------------------------------------------------+
| H400 |
+---------------------------------------------------------+
1 row in set (0.00 sec)
资料来源:
例如,在Oracle PL/SQL中,字符串具有相同的SOUNDEX,并且SOUNDEX无法区分:
select soundex ('The weather is nice, see http://xyz56.com') from dual;
SOUNDEX('THEWEATHERISNICE,SEEHTTP://XYZ56.COM')
-----------------------------------------------
T362
1 row selected.
select soundex ('The weather is nice, see http://xyz31.com') from dual;
SOUNDEX('THEWEATHERISNICE,SEEHTTP://XYZ31.COM')
-----------------------------------------------
T362
1 row selected.
我的建议是使用,这是字符串相似性的度量。要让MySQL直接计算,您必须在存储过程中实现它,这里有一个示例:
PHP和Java也有常见的实现。MySQL的Levenshtein距离算法: 请参阅: Levenshtein距离 两个字符串之间的Levenshtein距离是将一个字符串转换为另一个字符串所需的最小操作数,其中一个操作可能是插入、删除或替换一个字符。Jason Rust在上发布了此MySQL算法
Levenshtein距离算法:Oracle PL/SQL实现 资料来源: 如果假设有一个名为EMPLOYEES的表,其列名为FIRST_NAME,类型为VARCHAR2,则可以很容易地找到Levenshtein Distance=1的记录,如下所示:
SELECT *
FROM employees alfa
WHERE EXISTS
(SELECT 'X'
FROM employees beta
WHERE ld (beta.first_name, alfa.first_name) = 1);
通过此查询,您可以在结果集的每一行中显示Levenshtein距离为1的第一个\u名称的列表:
SELECT a.first_name, b.first_name
FROM employees a
INNER JOIN
employees b
ON ld (b.first_name, a.first_name) = 1;
例如:
SELECT DISTINCT a.first_name, b.first_name
FROM employees a
INNER JOIN
employees b
ON ld (b.first_name, a.first_name) <= 2
AND ld (b.first_name, a.first_name) > 0;
FIRST_NAME;FIRST_NAME_1
Jean;John
Nancy;Vance
Alana;Allan
Alana;Clara
Ellen;Eleni
John;Jean
Daniel;Danielle
Danielle;Daniel
Shelley;Shelli
Sundita;Nandita
Lisa;Luis
Stephen;Steven
Nanette;Janette
Diana;Alana
TJ;Ki
Luis;Lisa
Sarath;Sarah
Louise;Luis
Ki;TJ
Allan;Ellen
Luis;Louise
Den;Lex
Clara;Alana
Matthew;Mattea
Shelli;Shelley
Sarah;Sarath
Girard;Gerald
Vance;Nancy
Mattea;Martha
Allan;Alana
Nandita;Sundita
Ellen;Allan
Jean;Den
Eleni;Ellen
Gerald;Girard
Lex;Den
Janette;Nanette
Steven;Stephen
Mattea;Matthew
Den;Jean
Martha;Mattea
Alana;Diana
Soundex可以在大多数数字变化的情况下工作,但并不总是有效,例如,对于以下用例:第1行:在我的iPhone上的原始Gangstaz上刚刚达到55级!点击链接加入我的帮派iphone ipod OG第2排:在我的iphone上刚刚达到全球战争在线35级!单击链接加入我的团队iphone ipod。对于每一行,字符串的长度和变化的位置是不同的,看起来很有希望,但我不知道如何在查询大量数据时应用这一点,而不仅仅是比较两个字符串?非常有趣的解决方案,Paul,恭维!我从来没有听说过Levenshtein距离,我发现它是一个非常有用的概念。如果你已经有很多可能是半重复的数据,那么没有办法有效地做到这一点。钝的方法将比较效率为二次的任何两个字符串。我认为最好的方法是删除重复数据或“修复”当前数据(修复效率可能很低),然后计算插入点上可能存在的半重复数据。更好的方法是不完全使用字符串差异,向模型中添加更多结构,以便可以依赖记录的存在,并使用您的db索引。在Oracle PL/SQL中,您可以轻松找到满足Levenshtein distance=1条件的记录,例如,使用SELECT*FROM employees alfa(存在)从employees beta中选择“X”,其中ld beta.first_name,alfa.first_name=1;谢谢我实现了这个过程,并运行了一个查询,计算了我所选结果的Levenshtein距离(20行到一个硬编码字符串)。这已经使运行时慢了大约四倍,所以不幸的是,我认为这不适合限制查询,特别是因为我有>20000行。
CREATE OR REPLACE FUNCTION ld -- Levenshtein distance
(p_source_string IN VARCHAR2,
p_target_string IN VARCHAR2)
RETURN NUMBER
DETERMINISTIC
AS
v_length_of_source NUMBER := NVL (LENGTH (p_source_string), 0);
v_length_of_target NUMBER := NVL (LENGTH (p_target_string), 0);
TYPE mytabtype IS TABLE OF NUMBER INDEX BY BINARY_INTEGER;
column_to_left mytabtype;
current_column mytabtype;
v_cost NUMBER := 0;
BEGIN
IF v_length_of_source = 0 THEN
RETURN v_length_of_target;
ELSIF v_length_of_target = 0 THEN
RETURN v_length_of_source;
ELSE
FOR j IN 0 .. v_length_of_target LOOP
column_to_left(j) := j;
END LOOP;
FOR i IN 1.. v_length_of_source LOOP
current_column(0) := i;
FOR j IN 1 .. v_length_of_target LOOP
IF SUBSTR (p_source_string, i, 1) =
SUBSTR (p_target_string, j, 1)
THEN v_cost := 0;
ELSE v_cost := 1;
END IF;
current_column(j) := LEAST (current_column(j-1) + 1,
column_to_left(j) + 1,
column_to_left(j-1) + v_cost);
END LOOP;
FOR j IN 0 .. v_length_of_target LOOP
column_to_left(j) := current_column(j);
END LOOP;
END LOOP;
END IF;
RETURN current_column(v_length_of_target);
END ld;
SELECT *
FROM employees alfa
WHERE EXISTS
(SELECT 'X'
FROM employees beta
WHERE ld (beta.first_name, alfa.first_name) = 1);
SELECT a.first_name, b.first_name
FROM employees a
INNER JOIN
employees b
ON ld (b.first_name, a.first_name) = 1;
SELECT DISTINCT a.first_name, b.first_name
FROM employees a
INNER JOIN
employees b
ON ld (b.first_name, a.first_name) <= 2
AND ld (b.first_name, a.first_name) > 0;
FIRST_NAME;FIRST_NAME_1
Jean;John
Nancy;Vance
Alana;Allan
Alana;Clara
Ellen;Eleni
John;Jean
Daniel;Danielle
Danielle;Daniel
Shelley;Shelli
Sundita;Nandita
Lisa;Luis
Stephen;Steven
Nanette;Janette
Diana;Alana
TJ;Ki
Luis;Lisa
Sarath;Sarah
Louise;Luis
Ki;TJ
Allan;Ellen
Luis;Louise
Den;Lex
Clara;Alana
Matthew;Mattea
Shelli;Shelley
Sarah;Sarath
Girard;Gerald
Vance;Nancy
Mattea;Martha
Allan;Alana
Nandita;Sundita
Ellen;Allan
Jean;Den
Eleni;Ellen
Gerald;Girard
Lex;Den
Janette;Nanette
Steven;Stephen
Mattea;Matthew
Den;Jean
Martha;Mattea
Alana;Diana