2010-06-29 84 views
5

這是一個後續到:
MySQL - Is it possible to get all sub-items in a hierarchy?MySQL - 處理這種分層數據的最佳方法?

我有一個任意深度的鄰接表模型表(我在我可以將其轉換爲嵌套集模型

我讀了關於如何使用嵌套集模型的MySQL數據,雖然它似乎變得越來越複雜並且非常複雜,以至於無法完成插入,更新和刪除等基本功能。

另一個博客展示瞭如何在鄰接列表模型中使用觸發器系統,以保存一個將每個對象與其祖先關聯起來的祖先表。


現在我需要能夠返回給定節點的所有子節點的列表,以更改或刪除它們。這種層次結構一旦創建就不會一直改變,但是會有大量的層次結構。

的三種方法我看到的是:

  1. 創建存儲過程時這將做到這一點返回所有孩子遞歸查詢。

  2. 轉換爲嵌套集模型這將需要進入複雜性,並可能創建一個存儲過程來添加,編輯和刪除。

  3. 在插入/刪除觸發器上創建祖先表以處理所有數據。

如果還有其他方法我不探索,請讓我知道,我會更新此列表。

+1

*我將這個添加爲評論而不是答案,因爲這是我最近纔看到的:我沒有使用這種方法,但我還沒有完全理解,但我認爲你可能至少會發現它很有趣。[[用連續分數嵌套間隔樹編碼](http://arxiv.org/ftp/cs/papers/0402/0402051.pdf)(Vadim Tropashko)看起來很有趣,而且是對[與Farey分數的嵌套間隔](http://arxiv.org/html/cs/0401014)。和所有的方法一樣,有利弊 - 這只是你可能想添加到你的列表中的東西。 – Mike 2010-07-04 10:55:47

+0

你有沒有考慮過不使用關係數據庫?我忍不住想像圖形數據庫這樣的東西會更好。 – 2010-07-06 15:15:10

+0

從未聽說過圖形數據庫?我也需要知道它在速度和優化方面的比較。這只是整個系統的一小部分,所以它必須與其餘的部分並駕齊驅。 – 2010-07-06 19:33:53

回答

4

Quassnoi已對嵌套集模型和鄰接列表模型運行了一些性能測試,並在其博客文章Adjacency list vs. nested sets: MySQL中記錄了結果和建議。執行摘要如下:

  • 嵌套集取得所有子節點或所有父節點的速度更快。
  • 如果您經常需要更新表格,則嵌套集合是一個壞主意。

以下是他的文章的結論:

在MySQL中,嵌套集合模型應該是首選,如果更新到hierarhical結構是罕見的,這是經濟實惠鎖定該表的時間的更新(在長桌上可能需要幾分鐘)。

這意味着使用MyISAM存儲引擎創建表,創建如上所述的GEOMETRY類型的邊界框,使用SPATIAL索引對其進行索引並在表中保留該級別。

如果對錶的更新頻繁或無法鎖定更新所隱含的很長一段時間,則應使用鄰接列表模型來存儲分層數據。

這需要創建一個函數來查詢表。

本文其餘部分將介紹如何定義表,實現查詢並提供性能測量。空間索引的使用是一個聰明的想法,可以提高嵌套集合模型的性能,這對您而言可能是新的。


如果你還在考慮不MySQL的方式,那麼你可能想看看PostgreSQL這是另一種免費的開源數據庫。 PostgreSQL支持以recursive common table expressions的形式進行遞歸查詢,這些查詢比在MySQL中更容易查詢葉面數據,並且還提供了更好的性能。 Quassnoi還寫了一篇文章Adjacency list vs. nested sets: PostgreSQL,顯示細節。我們在談論其他方法時,Oracle的數據庫也值得一提。 Oracle還有一個自定義擴展CONNECT BY,它可以非常簡單快速地查詢葉面數據。 Quassnoi的文章Adjacency list vs. nested sets: Oracle再次涵蓋了性能細節。你需要讓所有的孩子查詢在這種情況下非常簡單:

SELECT * 
FROM yourtable 
START WITH id = 42 
CONNECT BY parent = PRIOR id 
2

我總是會用嵌套剪切簡單和快速。我總是建議this article。它顯示了使用這種分層數據工作所需的查詢。我在這裏看到的唯一缺點是,當hierachry達到一定的複雜程度時,插入/更新新記錄的速度可能會變慢,但閱讀速度比我見過的許多其他解決方案更快。

只給你從上面的文章爲例:

SELECT t1.name AS lev1, t2.name as lev2, t3.name as lev3, t4.name as lev4 
FROM category AS t1 
LEFT JOIN category AS t2 ON t2.parent = t1.category_id 
LEFT JOIN category AS t3 ON t3.parent = t2.category_id 
LEFT JOIN category AS t4 ON t4.parent = t3.category_id 
WHERE t1.name = 'ELECTRONICS'; 

+-------------+----------------------+--------------+-------+ 
| lev1  | lev2     | lev3   | lev4 | 
+-------------+----------------------+--------------+-------+ 
| ELECTRONICS | TELEVISIONS   | TUBE   | NULL | 
| ELECTRONICS | TELEVISIONS   | LCD   | NULL | 
| ELECTRONICS | TELEVISIONS   | PLASMA  | NULL | 
| ELECTRONICS | PORTABLE ELECTRONICS | MP3 PLAYERS | FLASH | 
| ELECTRONICS | PORTABLE ELECTRONICS | CD PLAYERS | NULL | 
| ELECTRONICS | PORTABLE ELECTRONICS | 2 WAY RADIOS | NULL | 
+-------------+----------------------+--------------+-------+ 
6 rows in set (0.00 sec) 

SQL明智的,我不認爲它可以得到任何漂亮和更簡單;)

我不知道到存儲過程的方式。但因爲它涉及遞歸(在你的情況下),我不知道它是否會在層次結構中的許多級別上快速。我假設你可以試試看。

+0

這是我用於嵌套集模型的文章。我遇到的問題是它會在您插入,更新或刪除時鎖定整個表。我不能那樣做。您爲鄰接列表模型顯示的另一種方法適用於已知深度。我有任意的深度。 – 2010-06-29 06:36:09

+0

我不認爲應該有必要進行鎖定。如果你使用InnoDB作爲引擎,你應該保持安全。 – DrColossos 2010-06-29 07:09:19

+0

現在就是MyISAM--你知道一個很好的參考資料,可以說明差異/優點/缺點嗎? – 2010-06-29 07:24:25

1

也許你應該考慮使用面向文檔的數據庫一樣MongoDB。它可以讓你的生活變得更容易。

+0

我不敢暗示這一點,但我完全同意。還要考慮像Tamino這樣的面向對象數據庫(http://www.softwareag.com/Corporate/products/wm/tamino/default.asp) – 2010-07-02 00:09:35

-1

我曾經不得不在一個類似於SQL的數據庫管理器中存儲一個複雜的分層任意深度物料清單系統,該系統並不真正完成任務,最終導致了混亂和棘手的索引,數據定義,查詢等。從頭開始重新啓動後,使用數據庫管理器爲簡單索引鍵上的記錄讀取和寫入提供一個API,並在外部代碼中執行所有實際輸入/操作/報告,最終結果更快實施,更容易理解,更容易維護和提高。所需的最複雜的查詢實質上是SELECT A FROM B.因此,不要將邏輯和操作嵌入到MySQL的限制之內,而是考慮敲出代碼來執行您想要的操作,並且僅依靠MySQL來實現最低級別獲取/看跌期權。

1

當處理分層數據集時,我發現最好先考慮緩存來處理它。這種以這種方式處理這個問題的主要好處之一就是它不需要將數據庫解除規範化爲可能難以改變的東西。

由於對於簡單的id -> data分辨率,內存堆(memcache,redis等)查找比SQL快得多,所以我會使用它們來緩存每個節點的直接子項的ID列表。這樣,您可以通過遞歸算法獲得不錯的性能,爲任何節點構建完整列表。

要添加/刪除新節點,您只需要使其「直接父緩存O(1)無效」。

如果速度不夠快,可以將另一層緩存添加到每個節點的節點的所有子節點的列表中。爲了使它適用於一個體面可變的數據集,您應該記錄每個節點的緩存性能(新鮮/緩存命中率),併爲緩存的存儲時間設置容差級別。這也可以存儲在內存堆中,因爲它不是重要數據。

如果你使用這個更高級的緩存模型,你將需要注意到這些完整的子節點列表將需要失效,當它的任何子節點被更改O(log n)

一旦你有你的孩子ID的列表,你可以使用SQL的WHERE id IN(id1, id2, ....)語法來查詢你想要的。

相關問題