2009-09-26 55 views
2

使用mysql和PHP如何实现搜索2个不同的表格数据?

我正在使用MATCH AGAINST子句。

它对单个表格工作正常。就像我想在商店的餐桌上搜索一样。没问题。

我想要的是能够在单个结果页面中搜索和显示来自不同表格的结果。

例如,如果i型 “巧克力衣服”

i可以得到4个结果如下:

Shop1结果

ShopItem1导致

ShopItem2导致

SHOP2结果

当然还有mos相关结果应该排在第一位。

我有很多问题。设计明智以及执行明智

1)我应该改变我的设计?我正在考虑创建一个名为搜索结果的单独表格,其中包含来自SHOPS和SHOPPRODUCTS表格的数据。但是这意味着我有一些数据重复。

2)我应该保持我目前的设计吗?如果是这样,那么我怎样才能通过两个不同表格的相关性对搜索结果进行排序?

我看到rottentomatoes在不同的组织他们的搜索结果。然而,我们更喜欢搜索结果不受不同类型的限制,特别是当我们有分页时,更难以导航UI。

http://www.rottentomatoes.com/search/full_search.php?search=girl

OR,实际上是最好的出路?

我希望有人可以给我这方面的指导,特别是如果你有在生成搜索结果的经验,看起来像多个表。

以来受需求,我就会把表结构在这里

CREATE TABLE `shopitems` (
    `id` int(10) unsigned NOT NULL auto_increment, 
    `ShopID` int(10) unsigned NOT NULL, 
    `ImageID` int(10) unsigned NOT NULL, 
    `name` varchar(100) NOT NULL, 
    `description` varchar(255) NOT NULL, 
    `pricing` varchar(45) NOT NULL, 
    `datetime_created` datetime NOT NULL, 
    PRIMARY KEY (`id`) 
) ENGINE=MyISAM AUTO_INCREMENT=31 DEFAULT CHARSET=utf8; 

/*Table structure for table `shops` */ 

DROP TABLE IF EXISTS `shops`; 

CREATE TABLE `shops` (
    `id` int(11) NOT NULL auto_increment, 
    `title` varchar(100) default NULL, 
    `description` text, 
    `keywords` text, 
    `url` varchar(255) default '', 

    `owner_id` varchar(255) default NULL, 
    `datetime_created` datetime default NULL, 
    `created_by` varchar(255) default NULL, 
    `datetime_modified` datetime default NULL, 
    `modified_by` varchar(255) default NULL, 

    `overall_rating_avg` decimal(4,2) default '0.00', 


    PRIMARY KEY (`id`), 
    FULLTEXT KEY `url` (`url`), 
    FULLTEXT KEY `TitleDescFullText` (`keywords`,`title`,`description`,`url`) 
) ENGINE=MyISAM AUTO_INCREMENT=3051 DEFAULT CHARSET=utf8; 

我打算通过描述和shopproducts表的名称列进行搜索。

但你可以看到它还没有实现。

虽然搜索商店已经启动并正在运行。

+2

添加表结构将有助于获得良好的答案 – 2009-09-26 10:44:22

+0

嗨您是什么意思?你的意思是我应该有一个名为search_results的单独表格,它包含所有现有的数据,并根据单独的表格进行匹配? – 2009-09-26 11:26:30

+0

依靠Sphinx或Xapian上的全文搜索不是更容易吗?以给定的时间间隔创建索引,并且只在其中搜索将大大提高搜索速度。 – unexist 2009-09-30 14:26:26

回答

5

这里有几个“游戏规则”,你必须记住解决这个问题。你可能已经知道这些,但明确地说明它们可能有助于确认其他读者。

  • MySQL中的所有索引都只能引用单个基表中的列。您无法创建索引跨多个表的全文索引。
  • 无法为视图定义索引,仅限于基表。
  • A MATCH()针对全文索引的查询必须按照索引中声明的顺序与全文索引中的所有列进行匹配。

我会创建第三个表来存储你想索引的内容。不需要冗余存储这些内容 - 仅将其存储在第三个表中。这借用了面向对象设计中的“公共超类”概念(只要我们可以将其应用于RDBMS设计)。

CREATE TABLE Searchable (
    `id` SERIAL PRIMARY KEY, 
    `title` varchar(100) default NULL, 
    `description` text, 
    `keywords` text, 
    `url` varchar(255) default '', 
    FULLTEXT KEY `TitleDescFullText` (`keywords`,`title`,`description`,`url`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8; 

CREATE TABLE `shopitems` (
    `id` INT UNSIGNED NOT NULL, 
    `ShopID` INT UNSIGNED NOT NULL, 
    `ImageID` INT UNSIGNED NOT NULL, 
    `pricing` varchar(45) NOT NULL, 
    `datetime_created` datetime NOT NULL, 
    PRIMARY KEY (`id`), 
    FOREIGN KEY (`id`) REFERENCES Searchable (`id`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8; 

CREATE TABLE `shops` (
    `id` INT UNSIGNED NOT NULL, 
    `owner_id` varchar(255) default NULL, 
    `datetime_created` datetime default NULL, 
    `created_by` varchar(255) default NULL, 
    `datetime_modified` datetime default NULL, 
    `modified_by` varchar(255) default NULL, 
    `overall_rating_avg` decimal(4,2) default '0.00', 
    PRIMARY KEY (`id`), 
    FOREIGN KEY (`id`) REFERENCES Searchable (`id`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8; 

注意唯一带有自动递增键的表现在是Searchable。表shopsshopitems使用具有兼容数据类型的密钥,但不能自动增量。因此,您必须在Searchable中创建一行以生成id值,然后才能在shopsshopitems中创建对应的行。尽管MyISAM将默默地忽略这些约束(并且您已经知道您必须使用MyISAM来支持全文索引),但为了说明的目的,我添加了FOREIGN KEY声明。

现在,您可以同时搜索shopsshopitems的文本内容在一个查询中,使用单个全文索引:

SELECT S.*, sh.*, si.*, 
    MATCH(keywords, title, description, url) AGAINST('dummy') As score 
FROM Searchable S 
LEFT OUTER JOIN shops sh ON (S.id = sh.id) 
LEFT OUTER JOIN shopitems si ON (S.id = si.id) 
WHERE MATCH(keywords, title, description, url) AGAINST('dummy') 
ORDER BY score DESC; 

当然,在Searchable给定的行只有一个表应匹配,无论是商店或商店,而这些表格有不同的栏目。因此,结果中的sh.*si.*将为NULL。您需要在应用程序中格式化输出。


其他一些答案建议使用Sphinx Search。这是另一项补充MySQL的技术,并增加了更复杂的全文搜索功能。它对查询有很好的表现,所以有些人对它很感兴趣。

但是创建索引,特别是增加索引是很昂贵的。事实上,更新Sphinx搜索索引成本太高,以至于推荐的解决方案是为较旧的归档数据创建一个索引,并为最近更可能更新的数据创建另一个较小的索引。然后每个搜索都必须针对两个单独的索引运行两个查询。如果你的数据不能自然适应旧数据不变的模式,那么你可能无法利用这个技巧。


回复您的评论:这是从Sphinx Search documentation约实时更新到索引的摘录:

有当 总数据集是太大,是 从头经常进行索引频繁的情况下,但新记录的数量相当小。 示例:一个论坛,其中包含1,000,000个 存档帖子,但每天只有1,000个新的 帖子。

在这种情况下,“活”(几乎实时 时间)更新索引可能是 使用所谓的 “主+三角”计划实施。

这个想法是,因为更新Sphinx搜索索引代价高昂,他们的解决方案是让索引更新尽可能小。因此,只有最近的论坛帖子(在他们的例子中),而较大的存档论坛帖子的历史从未改变,因此您为该收藏集建立第二个更大的索引一次。当然,如果你想做一个搜索,你必须查询两个索引。

定期地说,每周一次,“最近的”论坛消息将被视为“已归档”,您必须将最近帖子的当前索引合并到归档索引,然后启动较小索引。他们的确表明合并两个Sphinx搜索索引比在更新数据之后重建索引更有效率。

但我的观点是,并非每个数据集都自然地落入一种从不改变的存档数据集的模式,而最新的数据更新频繁。

以您的数据库为例:您有商店和商店。你怎么能把它们分成永不改变的行,而不是新行?应允许目录中的任何商店或产品更新其描述。但是,由于每次进行更改都需要重新构建整个Sphinx搜索索引,因此这将成为一项非常昂贵的操作。也许你会排队修改并在批处理中应用它们,每周重建一次索引。但试着向商店卖家解释为什么他们的商店描述的细微变化直到周日晚上才会生效。

+0

我通常不建议在查询结果中使用*选择器。这似乎是一个很好的主意,但它通常会阻碍向前兼容处理结果的软件。 – 2009-10-04 15:32:58

+0

@Matthieu M:是的,我同意,我只在通用查询和StackOverflow示例中使用通配符。我不使用生产代码的通配符。但是这个问题与全文搜索问题是正交的。 – 2009-10-04 16:18:05

+0

嗨比尔,谢谢你的回答。这是非常清楚,它是照明。虽然我有一些关于狮身人面像搜索的问题。 “事实上,更新Sphinx搜索索引的代价非常高,以至于建议的解决方案是为较旧的归档数据创建一个索引,为最近更新的数据创建另一个较小的索引,然后每个搜索必须运行两个查询,如果你的数据本身不适用于旧数据模式,那么你可能无法利用这个技巧。“你能详细说明这个部分吗? – 2009-10-04 16:31:40

0

我建议你第一个选择。冗余并不总是邪恶的。

所以我会做这样的一个表:

CREATE TABLE search_results 
(
    ... 
    `searchable_shop_info` VARCHAR(32), 
    `searchable_shopitem_info` TEXT 
    FULLTEXT KEY `searchable` (`searchable_shop_info`, `searchable_shopitem_info`) 
) Engine=MyISAM; 

那么你仍然可以使用SELECT * FROM search_results WHERE MATCH ( searchable_shop_info , searchable_shopitime_info ) AGAINST ('search query string');

+0

搜索结果我可以问你为什么建议在其他选项? – 2009-09-26 11:51:42

0

嗯也许ü可以用工会吗?像

 
create table search1 (
    title varchar(12), 
    relavency tinyint unsigned 
); 

create table search2 (
    title varchar(12), 
    relavency tinyint unsigned 
); 

insert into search1 values (substring(md5(rand()), 1, 12), (rand()*100)), 
(substring(md5(rand()), 1, 12), (rand()*100)), 
(substring(md5(rand()), 1, 12), (rand()*100)), 
(substring(md5(rand()), 1, 12), (rand()*100)), 
(substring(md5(rand()), 1, 12), (rand()*100)), 
(substring(md5(rand()), 1, 12), (rand()*100)), 
(substring(md5(rand()), 1, 12), (rand()*100)), 
(substring(md5(rand()), 1, 12), (rand()*100)), 
(substring(md5(rand()), 1, 12), (rand()*100)), 
(substring(md5(rand()), 1, 12), (rand()*100)), 
(substring(md5(rand()), 1, 12), (rand()*100)); 

insert into search2 values (substring(md5(rand()), 1, 12), (rand()*100)), 
(substring(md5(rand()), 1, 12), (rand()*100)), 
(substring(md5(rand()), 1, 12), (rand()*100)), 
(substring(md5(rand()), 1, 12), (rand()*100)), 
(substring(md5(rand()), 1, 12), (rand()*100)), 
(substring(md5(rand()), 1, 12), (rand()*100)), 
(substring(md5(rand()), 1, 12), (rand()*100)), 
(substring(md5(rand()), 1, 12), (rand()*100)), 
(substring(md5(rand()), 1, 12), (rand()*100)), 
(substring(md5(rand()), 1, 12), (rand()*100)), 
(substring(md5(rand()), 1, 12), (rand()*100)); 

(select *, 'search1' as source from search1) 
union (select *, 'search2' as source from search2) 
order by relevancy desc;

选择你的行,并按照正常计算相关性然后结合结果。我不知道,如果我明白你的方式错误,因为没有人似乎想到工会?

alt text

更新1:

好吧,我重新阅读乌尔问题和意见已经...我认为

1)我应该改变我的设计?我是 想拥有一个单独的表 称为搜索结果,将 包含来自SHOPS和 SHOPPRODUCTS表的数据。但这意味着 我有一些数据重复。

我想你应该使用一个视图来包含来自两个表的数据,因为视图会在你的数据发生变化时自动“更新”。如果你使用表格,你可能需要自己更新它。

CREATE VIEW viewSearch (Title, Relavency, SourceTable) AS 
(SELECT title, relavency, 'search1' as source FROM search1 
ORDER BY relavency DESC 
LIMIT 10) 
UNION 
(SELECT title, relavency, 'search2' as source FROM search2 
ORDER BY relavency DESC 
LIMIT 10) 
ORDER BY relavency DESC 
LIMIT 10;

alt text

2)我应该保持我目前的设计?如果 那么,那么我怎样才能得到 搜索结果排序相关性 横跨2个不同的表?

由SQL/View上面你可以。基本上通过将

... 
ORDER BY relavency DESC 
LIMIT 10

我很好奇。这意味着我需要运行 查询EVERYTIME的任何搜索 输入。因为不同的输入 有不同的相关性分数。

我真的不明白你的意思吗?如果你现在在2个表之间进行搜索,那么你是否会做2个单独的SQL查询(每个表有1个)?或者如果你要选择结果到1表中它仍然...实际上3个查询(2选择到结果表,然后1查询)。

我还在每个SELECT中添加了ORDER BY & LIMIT,通过减少记录来加速进程。然后再次订购& LIMIT。

在这个例子中,我不知道你如何计算相关性,所以我使用了随机数。

也许 我有点缺乏理解。我怀疑你的方法是否是 资源密集。请指教 我。我愿意考虑所有的 的可能性。

我不太确定是诚实的,但想知道这个答案...我猜测它仍然会比多个查询更好。

哦,我还没有真正熟悉的全文搜索,所以我不知道,如果这种方法会造成任何影响

+0

我很好奇。这意味着我需要为任何搜索输入运行该查询EVERYTIME。因为不同的投入会有不同的相关性分数。 也许我有点缺乏理解。我怀疑你的方法是否是资源密集型的。请赐教。 我愿意考虑所有的可能性。 – 2009-09-26 16:05:32

+0

感谢您的努力。但你还没有进行全文搜索,所以我不认为你会看到问题。我很肯定你不能在VIEW上进行全文搜索。 – 2009-09-27 12:45:54

+0

嗯,好吧,我不知道你将如何维护结果表。但我想触发器将是一个选项 – iceangel89 2009-09-27 13:14:02

0

如果我理解大家的提问,答案很简单:

  1. 别改变设计。这很好。这就是它应该如此。
  2. 做连接查询是这样的:
 
SELECT * FROM shops 
LEFT OUTER JOIN shopitems ON (shopitems.shopid = shops.id) 
WHERE 
    MATCH (shops.title, shops.description, shops.keywords, 
      shopitems.name, shopitems.description) 
    AGAINST ('whatever text') 
+0

1)你理解错误。 2)查询甚至不起作用,更不用说我的问题了。 – 2009-10-01 03:28:37

1

我不知道我理解正确的,但这里是我的2美分。

从我所看到的,问题是,你有2代表具有非常不同的布局,所以我会假设你要基于这些字段的全文搜索:用于商店

  • :标题,描述和关键字
  • shopitems:名称和说明

解决方案1:布局的一致性 - 母鹿s不使用索引...

如果您可以以某种方式更改您的商品列的名称,它会立即变得更简单。

Select id From 
(Select id, text1, text2, text3 From table1 
UNION 
Select id, text1, text2, text3 From table2) 
Where MATCH(id, text1, text2, text3) AGAINST('keyword1 keyword2 keyword3') 

但是我可以理解,改变已经存在的一切将是不切实际的。请注意,在使用别名的情况下,添加第三个(虚拟)文本列到shopitems可以做到这一点。

解决方案2:后处理

我应该此话该计算出的值实际上可以返回(并因此使用)。因此,您可以使用此值创建临时表!请注意,如果您想返回“标题”和“说明”两栏应具有相同类型的高校统战方式来处理......

Select id, title, description From 
(
Select id, title, description, MATCH(id, title, description, keywords) AGAINST('dummy') As score 
     From shops 
     Where MATCH(id, title, description, keywords) AGAINST('dummy') 
UNION 
Select id, name As title, description, MATCH(id, name, description) AGAINST('dummy') As score 
     From shopitems 
     Where MATCH(id, name, description) AGAINST('dummy') 
) 
ORDER BY score DESC 

我没有这个查询的性能的想法尽管如此,我不知道MySQL是否会优化每一个Selects的双重调用MATCH/AGAINST(我希望它)。

问题是我的查询只是一个示范。使用别名的缺点是,现在你不知道他们来自哪个表。

无论如何,我希望它对你有所帮助。

+0

谢谢。我认为你的答案至少比其他答案更有意义。 我会至少给你一个upvote。其他答案是,我觉得,从臀部的风格拍摄..令人失望。 – 2009-10-02 10:54:25

+0

这两个解决方案都有一个id冲突的问题,但可以通过向每个表添加另一个字段并将该表的名称放入该字段中的所有行来解决。然而,这也意味着,当我在网页上显示我的结果时,我必须再次检索所有关联信息,因为我只有id。 – 2009-10-02 11:01:23

+0

是的双重检索的问题令人讨厌,这就是为什么我建议尝试拥有更多类似的表格布局,如果可能的话。请注意,在第二种解决方案中,您可以要求检索更多信息(标题,说明)并通过别名平滑差异。如果您告诉我您的每张表格需要哪些行以及您准备在您的表格结构上进行哪些更改,我可以尝试想出更完整的解决方案。 – 2009-10-02 12:46:08

0

我会去参加联盟。这是声明的目的。

0

我会去你的第一个选择,创建一个单独的搜索表。

当我们需要在多个SOA系统中搜索数据时,我们已经完成了这一次。

这种方法的好处是:

  • 更快的响应搜索请求通过搜索的组织
  • 更多的控制效果

的缺点是:

  • 时间较慢保存数据,因为它必须写两个地方
  • 用于存储数据的额外空间
相关问题