假設您有一個存儲有序樹層次結構的平面表: sql
Id Name ParentId Order 1 'Node 1' 0 10 2 'Node 1.1' 1 10 3 'Node 2' 0 20 4 'Node 1.1.1' 2 10 5 'Node 2.1' 3 10 6 'Node 1.2' 1 20
這是一個圖,咱們有[id] Name
。 根節點0是虛構的。 數據庫
[0] ROOT / \ [1] Node 1 [3] Node 2 / \ \ [2] Node 1.1 [6] Node 1.2 [5] Node 2.1 / [4] Node 1.1.1
您將使用哪一種簡約方法將其做爲正確排序,正確縮進的樹輸出到HTML(就此而言,仍是文本)? 數組
進一步假設您只有基本的數據結構(數組和哈希圖),沒有帶有父/子引用的奇特對象,沒有ORM,沒有框架,只有兩隻手。 該表表示爲結果集,能夠隨機訪問。 數據結構
可使用僞代碼或簡單的英語,這純粹是一個概念性問題。 框架
額外的問題:在RDBMS中是否存在從根本上更好的方法來存儲這樣的樹結構? nosql
編輯和添加 工具
要回答一個評論者( Mark Bessey )的問題:根節點不是必需的,由於它永遠不會顯示。 ParentId = 0是表示「這些是頂級」的約定。 Order列定義如何對具備相同父代的節點進行排序。 spa
我所說的「結果集」能夠圖片爲一個哈希表數組(保留在該術語中)。 對於個人示例,本應已經存在。 一些答案須要付出額外的努力,而後再進行構建,但這不要緊。 code
這棵樹能夠任意深。 每一個節點能夠有N個子節點。 不過,我並無真正想到「成千上萬的條目」樹。 對象
不要將我對節點命名(「節點1.1.1」)的選擇誤認爲是要依賴的東西。 這些節點也能夠稱爲「 Frank」或「 Bob」,沒有暗示命名結構,這僅僅是爲了使其可讀。
我已經發布了本身的解決方案,所以大家能夠將它分解成碎片。
考慮將noeo工具(例如neo4j)用於層次結構。 例如,諸如linkedin之類的聯網應用程序使用couchbase(另外一個nosql解決方案)
可是,僅將nosql用於數據集市級別的查詢,而不用於存儲/維護事務
假設您知道根元素爲零,這是要輸出到文本的僞代碼:
function PrintLevel (int curr, int level) //print the indents for (i=1; i<=level; i++) print a tab print curr \n; for each child in the table with a parent of curr PrintLevel (child, level+1) for each elementID where the parentid is zero PrintLevel(elementID, 0)
若是能夠建立嵌套的哈希圖或數組,那麼我能夠簡單地從表頭開始將表格添加到嵌套數組中。 我必須跟蹤每行到根節點,以便知道要插入嵌套數組中的哪一個級別。 我可使用記憶,這樣我就不須要一遍又一遍地查找同一個父母。
編輯:我將首先將整個表讀入數組,所以不會重複查詢數據庫。 固然,若是您的桌子很大,這將不切實際。
構建完結構後,我必須先遍歷深度並打印出HTML。
沒有更好的基本方法來使用一個表存儲此信息(儘管我可能錯了;),而且但願看到一種更好的解決方案)。 可是,若是您建立一個方案來使用動態建立的db表,那麼您將在犧牲簡單性和SQL地獄的風險的同時開闢了一個全新的世界;)。
您可使用哈希圖模擬任何其餘數據結構,所以這不是一個可怕的限制。 從頂部到底部掃描,您將爲數據庫的每一行建立一個哈希圖,併爲每一列建立一個條目。 將每一個哈希表添加到鍵入ID的「主」哈希表。 若是任何節點具備還沒有出現的「父」節點,請在主哈希圖中爲其建立一個佔位符條目,並在看到實際節點時將其填充。
要打印出來,請對數據進行簡單的深度優先傳遞,並始終跟蹤縮進級別。 經過爲每一行保留一個「子項」條目,並在掃描數據時填充它,可使此過程變得更加容易。
至因而否有一種「更好」的方式將樹存儲在數據庫中,這取決於您如何使用數據。 我已經看到了具備已知最大深度的系統,該系統爲層次結構中的每一個級別使用了不一樣的表。 若是樹中的級別畢竟不徹底相等(頂層類別與葉子不一樣),這頗有道理。
好的選擇,我會使用對象。 我會爲每一個記錄建立一個對象,其中每一個對象都有一個children
對象集合,並將它們所有存儲在ID爲鍵的assoc數組(/ hashtable)中。 並快速瀏覽該收藏集,將子級添加到相關的子級字段中。 簡單。
可是因爲限制某些良好的OOP的使用對您很無聊,所以我可能會根據如下條件進行迭代:
function PrintLine(int pID, int level) foreach record where ParentID == pID print level*tabs + record-data PrintLine(record.ID, level + 1) PrintLine(0, 0)
編輯:這相似於其餘幾個條目,但我認爲它稍微乾淨一些。 我要添加的一件事:這是很是消耗SQL的。 真討厭 若是能夠選擇,請執行OOP路線。