2009-09-26 52 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系統中搜索數據時,我們已經完成了這一次。

這種方法的好處是:

  • 更快的響應搜索請求通過搜索的組織
  • 更多的控制效果

的缺點是:

  • 時間較慢保存數據,因爲它必須寫兩個地方
  • 用於存儲數據的額外空間
相關問題