[英]Implementing a hierarchical data structure in a database
我知道有兩種方法:鄰接列表和嵌套樹。 據說由於大量查詢,鄰接列表在遍歷上使用會很慢。 但我不知道這方面的任何實際數字。 我正在制作的網站將有200頁。 遍歷生成(例如)站點地圖需要花費超過0.3秒的時間嗎?
使用LAMP堆棧在MySQL(innoDB)上運行。
如果可能的話,我更願意實現鄰接,因為設計更簡單。
謝謝。
除了你提到的兩個選項之外,還有更多的選擇。 有:
請參閱我的回答“ 什么是將平台解析成樹的最有效/優雅的方法? ”
或者幾本書:
MySQL中管理分層數據的文章詳細介紹了這一點。
我建議使用“嵌套集”技術,因為它允許您在一個查詢中獲取整個樹(及其子節點)。 基本上讀取是便宜的,但寫入是昂貴的,因為整個樹必須重新平衡。 但是如果你有99%的讀數,那么它是完全合理的。
解析鄰接列表的天真方法需要大量查詢,而對於大型列表,可能需要花費大量時間來構建內存。 作為參考,我所指的天真方法可以概括為:選擇沒有父項的所有項目,然后為每個項目遞歸地獲取它的子項。 此方法需要n + 1個數據庫查詢。
我使用以下方法構建一個帶有1個查詢的鄰接列表。 從數據庫中選擇所有項目。 將所有項目轉移到由其鍵索引的數組中。 遍歷數組並將父對象的引用分配給每個子對象。 第二次遍歷數組並刪除僅留下根級對象的所有子對象。
既然你提到了LAMP堆棧,那么PHP代碼大致如下:
<?php
// Assumes $src is the array if items from the database.
$tmp = array();
// Traverse the array and index it by id, ensuing each item has an empty array of children.
foreach ($src as $item) {
$item['children'] = array();
$tmp[$item['id']] = $item;
}
// Now traverse the array a second time and link children to their parents.
foreach ($tmp as $id => $item) {
if ($item['parent_id'] != 0 || $item['parent_id'] !== NULL) {
$tmp[$item['parent_id']]['children'][$id] = &$tmp[$id];
}
}
// Finally create an array with just root level items.
$tree = array();
foreach ($tmp as $id => $item) {
if ($item['parent_id'] == 0 || $item['parent_id'] === NULL) {
$tree[$id] = $item;
}
}
// $tree now contains our adjacency list in tree form.
?>
請注意,此代碼旨在說明從單個數據庫查詢構建鄰接列表的技術。 它可能可以針對更少的內存消耗等進行優化。它還沒有經過測試。
吉姆
我認為另一種方法稱為“嵌套集”,而不是“嵌套樹”。
無論如何,站點地圖的一個好處是你可能知道它的最大深度。 我認為鄰接模型的問題是相應的SQL一次只能在一個層面上工作,所以如果你有'n'個級別,那么你需要一個'n'個SQL語句的循環...但我認為(我'我不確定)如果您事先知道最大'n',那么您可以編寫相應的固定數量級別的SQL。
0.3秒聽起來像很長一段時間才能看到200頁,所以這可能還行。
站點地圖也不經常更新; 因此,即使從SQL檢索確實需要很長時間,您也可以將檢索/計算的樹緩存在RAM中。
或者,不要擔心構建樹的SQL,您可以盡可能簡單地存儲它(作為鄰接列表),從數據庫中將其作為一組簡單的行檢索,並在RAM中構建樹(使用循環)您的高級編程語言)而不是使用SQL中的循環來使用SQL語句構建樹。
為了完整性:Oracle具有START_WITH
和CONNECT_BY
運算符:請參閱此分層查詢文檔。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.