1. 程式人生 > >標籤(Tag)的資料庫設計

標籤(Tag)的資料庫設計

譯者注:本文在涉及到專業術語或者譯者表達不明白的地方均會保留原英文。

最近,在del.icio.us mailinglist(譯者按:應該是美味書籤的討論版塊。以下del.icio.us翻譯為美味書籤)上面發了一個問題:“有人知道美味書籤的資料庫設計嗎?”。之後我得到了一些回覆,所以我想把這部分東西的知識分享給大家。

疑問

當你要為一個書籤新增你認為需要的一個或多個標籤時(或日誌或其他)其資料庫是如何設計的?然後,執行查詢時取消這些書籤中標籤的合集(union)或交集(intersection)。也能從搜尋結果中減少一些標籤。

大致有三種不同的解決方案:(注意:如果你開發了一個網站使得任何人都可以新增標籤,而且是一個較大規模的網站則請務必看下其作者寫的另外一篇文章:

標籤系統的效能測試

“MySQLicious” 方法(solution)

在這個方法中僅架構了一個表,它是去規範化(denormalized)表。

這個型別被叫做“MySQLicious 方法(solution)”,因為MySQLicious使用這種結構可以把美味書籤的資料匯入到一個表中。

譯者注:MySQLicious是一個把del.icio.us書籤映象到MySQL資料庫中的工具。

交集(AND)

查詢方式: “search+webservice+semweb”:

合集(OR)

查詢方式: “search|webservice|semweb”:

減少(Exclusion)

查詢方式: “search+webservice-semweb”

結論

優點:

  • 只用一個表。
  • 查詢方式簡單易懂。
  • 一次就能獲得全文搜尋結果,可能速度快一些。
  • 我猜測查詢在基於良好引數下是相當的 快(Peter Cooper的部落格也提到:去規範化!去規範化!去規範化!) 慢的。全文搜尋會稍微提速,我做了一個我的配置測試來驗證它。

缺點:

  • 每個書籤的標籤數量是有限的。通常情況下是在資料庫中使用一個256位元組的域(VCHAIR),否則,假設你用Text或類似的域,則速度將會慢下來。
  • 如果你注意了(就像Patrice那樣)你會發現當你以“websearch”使用Like “%search”搜尋標籤時它也能搜尋到,當你修改並使用Like “%search%”時你最終必須使用一個混亂的解決方法:在標籤頭新增一個空格,這樣才能使其工作。

分離(Scuttle)欄位,並歸類到兩個表中。右圖表“scCategories”是一個標籤表,通過一個外來的ID連結書籤表。

交集(AND)

查詢方式:“bookmark+webservice+semweb”:

首先,當搜尋的標籤為“bookmark”,“webservice“或“semweb”(例如:c.category IN ('bookmark', 'webservice', 'semweb'))時所有名為"bookmark"的標籤都會被搜尋,然後所有包含這三個標籤的書籤將篩選出來 (HAVING COUNT(b.bId)=3).

合集(OR)

查詢方式:“bookmark|webservice|semweb”:
Just leave out the HAVING clause and you have union:

減少(Exclusion)

查詢方式:“bookmark+webservice-semweb”,意味著:bookmark AND webservice AND NOT semweb.

省略掉 HAVING COUNT 會導致搜尋方式變為“bookmark|webservice-semweb”.
資訊來源:Rhomboid寫的helping me out with this query.

結論

我猜測這個解決方案主要有利點是使得他更正常化,比第一個解決方案比較而言,好處在於可以為每一個書籤新增無限數量的標籤。

Toxi 提出了一個三個表的結構,通過表”tagmap“的書籤和標籤的n-to-m關聯。每一個標籤都可以在不同的書籤一期使用,反之亦然。這種資料庫結構也被用在Wordpress之中。

交集(AND)

查詢方式:“bookmark+webservice+semweb”

合集(OR)

查詢方式:“bookmark|webservice|semweb”

減少(Exclusion)

查詢方式:“bookmark+webservice-semweb”,意味:bookmark AND webservice AND NOT semweb.

省略掉 HAVING COUNT 會導致搜尋方式變為“bookmark|webservice-semweb”.
資訊來源:Rhomboid寫的helping me out with this query.

結論

優點:

  • 可以為每個標籤節省額外的資訊(描述,分類等)
  • 這是一個最正常化的解決方案(即,第三正規化:3NF

缺點:

  • 當修改或刪除書籤後,需要刪除中間表的相應資料(When altering or deleting bookmarks you can end up with tag-orphans)。

如果你想要更復雜的查詢,比如”(bookmarks OR bookmark) AND (webservice or WS) AND NOT (semweb or semanticweb)“這樣的查詢語句,我建議參見以下查詢/計算過程:

  1. 為每一個標籤出現在你的”“tag-query”時執行一個查詢(Run a query for each tag appearing in your “tag-query”):SELECT b.id FROM tagmap bt, bookmark b, tag t WHERE bt.tag_id = t.tag_id AND b.id = bt.bookmark_id AND t.name = "semweb"
  2. 把每一個編號集從結果中導到一個數值裡面(使用你喜歡的編碼語言),這樣可以快取你想要的陣列。
  3. 使用合集或交集或其他方式限制陣列。

通過這種方式,你也可以查詢"(del.icio.us|delicious)+(semweb|semantic_web)-search",這種型別的查詢(即,括號內)利用去規範化的“MySQLicious solution”不能這樣做。
這是最靈活的資料結構和我猜想它的效果相當好(即,使用一些快取技術)。

2006年5月更新:這篇文章獲得了大家的注視。我真的不是為此而準備的!看來大家不斷的提到了他,甚至一些網站轉載我的文章,我認為,這些不同方式的理論的知識應該歸功於:MySQLicious, scuttle, Toxi以及所有參與並貢獻的評論者(請務必閱讀!)

p.s. 感謝Toxi發給我關於三個表結構的疑問,Benjamin Reitzammer為我指點的文章(一個很好的標籤查詢參考)和powerlinux提供的scuttle指引。

擴充套件閱讀