如何找到mySQL行之间的相似性?

时间:2011-08-03 13:04:26

标签: php mysql

我正在尝试创建一个脚本,在我的表行之间找到匹配的百分比。例如,表 products 中的mySQL数据库包含字段 name (索引,FULLTEXT),其值为

LG 50PK350 PLASMA TV 50" Plasma TV Full HD 600Hz 
LG TV 50PK350 PLASMA 50"
LG S24AW 24000 BTU
Aircondition LG S24AW 24000 BTU Inverter

您可能会看到所有这些关键字都有相同的关键字。但第一个名字和第二个名字更相似。此外,第3和第4关键字之间的关键字比第1和第2更相似。

我的mySQL数据库有数千个产品名称。我想要的是找到那些具有超过百分比(比如说60%)相似性的名称。

例如,正如我所说,第一个,第二个(以及任何其他名称)在它们之间匹配超过60%,将以组样式格式回应,让我知道这些产品是相似的。第三和第四,以及任何其他超过60%的匹配将在另一组后回复,告诉我这些产品匹配。

如果可能的话,回复满足所有分组匹配名称的关键字会很棒。例如 LG S24AW 24000 BTU 是包含在第3和第4名称中的关键字。

最后,我将创建所有这些关键字的列表。

我现在所拥有的是以下查询(正如Jitamaro建议的那样)

Select t1.name, t2.name From products t1, products t2

在所有其他名称旁边创建一个新名称字段。对不起,我不知道如何解释它,但这就是它的作用:(真正的价值观是上面的产品名称)

在查询之前

-name-
A
B
C
D
E

查询后

-name- -name-
A        A
B        A
C        A
D        A
E        A
A        B
B        B
C        B
D        B
E        B
.
.
.

有没有办法用mySQL或PHP找到匹配的名称并提取关键字,如上所述?请分享代码示例。

谢谢社区。

17 个答案:

答案 0 :(得分:6)

使用LIKE OR REGEXP查询数据库:

SELECT * FROM product WHERE product_name LIKE '%LG%';
SELECT * FROM product WHERE product_name REGEXP "LG";

循环结果并使用similar_text():

$a = "LG 50PK350 PLASMA TV 50\" Plasma TV Full HD 600Hz"; // DB value
$b = "LG TV 50PK350 PLASMA 50\"" ; // USER QUERY

$i = similar_text($a, $b, $p);
echo("Matched: $i  Percentage: $p%");

//outputs: Matched: 21 Percentage: 58.3333333333%

您的第二个示例与62.0689655172%匹配:

$a = "LG S24AW 24000 BTU"; // DB value
$b = "Aircondition LG S24AW 24000 BTU Inverter" ; // USER QUERY

$i = similar_text($a, $b, $p);
echo("Matched: $i  Percentage: $p%");

您可以定义高于40%的百分比来匹配产品 请注意,similar_text()是大小写SensitivE所以你应该小写字符串。

答案 1 :(得分:3)

关于你的第二个问题,levenshtein()函数(in MySQL)将是一个很好的候选人。

答案 2 :(得分:2)

当我查看您的示例时,我会考虑如何根据标题找到类似的产品。从你的两个例子中,我可以看到每一行中有一件事突出其他任何东西:型号。 50PK350可能不会出现在与此模型相关的任何地方。

现在,MySQL本身并不是为处理这样​​的问题而设计的,而是它上面的一些螺栓连接工具。部分问题是在所有位置查询所有这些字段都很昂贵。你真的想以某种方式拆分它并索引它。 Lucene的相似性类别会对很少出现在所有数据中的单词给予高分,但确实会显示为您数据的高百分比。见High level explanation of Similarity Class for Lucene?

您还应该查看Comparison of full text search engine - Lucene, Sphinx, Postgresql, MySQL?

针对Lucene相似性类对每个单词进行评分应该更快,更可靠。您的分数总和应该为您提供最相关的产品。对于电视,我希望首先看到完全匹配,然后是其他一些相同尺寸,然后是品牌,一般电视等等。

无论你做什么,都要意识到除非你通过在SQL系统之上使用另一个工具来改变数据结构来创建更好的数据结构,否则你的查询将会太慢且昂贵。我认为Lucene可能是要走的路。 Sphinx或其他未提及的选项也可能需要考虑。

答案 3 :(得分:1)

这比看起来更棘手,你的帖子中缺少信息:

  • 人们如何使用此自动完成功能?
  • 您是否可以找到产品的所有名称?因为显然不是所有的商店都以类似的方式命名他们的产品,所以店员可能无法找到他找到的产品。
  • 您是否有关于同一产品的产品名称的信息?
  • 您正在搜索哪家商店是否相关?这个自动完成的用途在哪里?
  • 自动完成是否真的只建议与您输入的字词所有匹配的产品? (在技术上,纠正错别字并不是那么难)

我认为您需要更清晰地了解您(或更好的用户)希望此自动完成功能执行的操作。

自动完成功能是一种用户友好型功能。它可能以模糊的方式帮助用户,因此没有单一的正确答案。你必须弄清楚最有效的方法,而不是技术上最容易做到的事情。

首先弄清楚你想要什么,然后担心技术。

答案 4 :(得分:1)

一种可能的解决方案是使用Damerau-Levenstein distance。它可以像这样使用

select *
from products p
where DamerauLevenstein(p.name, '*user input here*')<=*X*

你必须弄清楚最能满足你需求的X.它应该是大于零的整数。您可以根据需要对其进行硬编码,参数化或计算。

这里最棘手的是DamerauLevenstein。它必须是存储过程,实现Damerau-Levenstein算法。我这里没有MySQL,所以我可能会在今天晚些时候为你写这篇文章。

更新: MySQL不支持存储过程中的数组,因此除非在每个函数调用中使用临时表,否则无法在MySQL中实现Damerau-Levenstein。这将导致糟糕的表现。所以你有两个选择:用Alix Axel建议的levenstein循环PHP中的结果,或者将数据库迁移到支持数组的PostgreSQL。 还有一个创建User-Defined函数的选项,但这需要在C中编写这个函数,将它链接到MySQL并可能重建MySQL,所以这样你只会增加更多头痛。

答案 5 :(得分:0)

这个问题与此类似:):

What is the best way to implement a substring search in SQL?

Trigram 可以轻松找到类似的行,在那个问题中我发布了 php + mysql + trigram解决方案

答案 6 :(得分:0)

我建议你使用一些全文搜索引擎,比如sphinx。它有可能实现您想要的任何算法。例如,您可以使用“quorom”或“any”搜索。

答案 7 :(得分:0)

这是一个聚类问题,可以通过数据挖掘方法解决。 (http://en.wikipedia.org/wiki/Cluster_analysis)它需要大量内存和计算密集型操作,不适合数据库引擎。否则,将不存在单独的数据挖掘,文本挖掘或业务分析软件。

答案 8 :(得分:0)

如果您的数据库只是将UPC代码作为其中一个字段,并且该字段维护良好,即您可以相信数据库维护者正确输入并正确反映了该项目的内容 - 那么您不需要做你建议的所有工作。

更好的想法可能是在下一个数据库中有一个UPC字段 - 并将其限制为唯一。

数据库用户试图将已经存在的UPC放入数据库 - 他们收到错误。

数据库保持其完整性。

如果这样的数据库保持其完整性 - 做你所建议的必要性永远不会出现。

这可能对您当前的任务(道歉)没有多大帮助 - 但对于未来类似的数据库 - 您可能希望考虑一下......

答案 9 :(得分:0)

听起来你已经经历了所有这些麻烦来解释一个复杂的场景,然后说你想忽略最佳答案,让我们给你“握手”协议(一切都与没有的东西相比较)还没有比较它。所以...伪代码:

select * from table order by id
while (result) {
    select * from table where id > result_id
}

那就行了。

答案 10 :(得分:0)

似乎你总是想要返回最短的字符串?这比任何事情都更重要或更具问题。但是你可能会有类似......

SELECT * FROM products LIMIT 1
WHERE product_name like '%LG%'
ORDER BY LENGTH(product_name) ASC

答案 11 :(得分:0)

如果要相互检查所有名称,则需要在mysql中进行交叉连接。有很多方法可以实现这一目标:

1. Select a, b From t1, t2

2. Select a, b From t1 Join t2

3. Select a, b From t1 Cross Join t2

然后你可以遍历结果。当我说用n ^ 2-(n-1)个元素创建一个2d数组并且每个元素相互连接时,这是相同的。

P.S。:选择t1.name,t2.name从产品t1,产品t2

答案 12 :(得分:0)

也许你想从2个字符串中找到最长的公共子字符串?然后,您需要为每个字符串计算后缀树,请参阅此处http://en.wikipedia.org/wiki/Longest_common_substring_problem

答案 13 :(得分:0)

你的方法似乎很合理。为了匹配类似的产品,我建议进行三元搜索。关于它如何与String::Trigram Perl模块一起工作,有一个相当不错的解释。

我建议使用trigram搜索来获取匹配列表,也可以根据您需要处理的数据量以及添加新产品的频率来进行一些人工审核。我发现这种方法在实践中运作良好。

答案 14 :(得分:-1)

好吧,我想我正在尝试实施非常相似的事情。它可以像谷歌Chrome地址框一样工作。当您键入地址时,它会为您提供建议。就我而言,这就是你想要实现的目标。

我无法给你一些确切的解决方案,但有些建议。

  1. 您需要实施下拉框,以便有人开始输入他们正在寻找的产品
  2. 然后你需要获取下拉列表的当前值,然后像上面发布的那样运行查询。可以是“SELECT * FROM product WHERE product_name LIKE'LG%';”
  3. 保存查询结果
  4. 刷新页面
  5. 将查询结果添加到下拉列表
  6. 注意:

    您需要将查询结果保存在某个地方,例如带有HTML代码的文本文件,即“选项”LG TS 600“/ option”(当然,将&lt;&gt;括号添加到选项中)。页面刷新后,此值将用于填充选项框。您需要为用户设置用户会话以获得相同用户的相同结果,否则如果更多用户同时使用搜索,则可能会发生冲突。因此,使用搜索ID和会话ID,您可以匹配它们。您可以将其保存在文件或表中。表会更方便。实际上,在我看来,整个子系统都是你在寻找什么。

    我希望它有所帮助。

答案 15 :(得分:-1)

这是另一个想法(但我投票给levenshtein()):

创建名称及其频率中使用的所有单词的临时表。

选择结果范围(最受欢迎的单词可能是LCD或LED等字样,大多数独特单词可能很好,它们可能是产品实际名称)。

建议每个结果词:

答案 16 :(得分:-1)

您可以使用LIKE在表格中查找类似的产品名称。例如:

SELECT * FROM product WHERE product_name LIKE 'LG%';