天天看點

SQL 雙親節點查找所有子節點

怎麼儲存樹狀結構的資料呢?在 SQL 中常用的是雙親節點法。建立表如下

其中,parent id 表示父節點, name 是節點名稱。

假設目前欲擷取某一節點下所有子節點(擷取後代 Descendants),該怎麼做呢?如果使用程式(Java/PHP)遞歸調用,那麼将在資料庫與本地開發語言之間來回通路,效率之低可想而知。于是我們希望在資料庫的層面就可以完成,——該怎麼做呢?

經查詢,最好的方法(個人覺得)是 SQL 遞歸 CTE 的方法。所謂 CTE 是 Common Table Expressison 公用表表達式的意思。網友評價說:“CTE 是一種十分優雅的存在。CTE 所帶來最大的好處是代碼可讀性的提升,這是良好代碼的必須品質之一。使用遞歸 CTE 可以更加輕松愉快的用優雅簡潔的方式實作複雜的查詢。”——其實我對 CTE 了解有效,大家谷歌下其意思即可。

怎麼用 CTE 呢?我們用小巧資料庫 SQLite,它就支援!别看他體積不大,卻也能支援最新 SQL99 的 with 語句,例子如下。

其中 WHERE id = 1 是那個父節點之 id,你可以改為你的變量。簡單說,遞歸 CTE 最少包含兩個查詢(也被稱為成員)。第一個查詢為定點成員,定點成員隻是一個傳回有效表的查詢,用于遞歸的基礎或定位點。第二個查詢被稱為遞歸成員,使該查詢稱為遞歸成員的是對 CTE 名稱的遞歸引用是觸發。在邏輯上可以将 CTE 名稱的内部應用了解為前一個查詢的結果集。遞歸查詢沒有顯式的遞歸終止條件,隻有當第二個遞歸查詢傳回空結果集或是超出了遞歸次數的最大限制時才停止遞歸。遞歸次數上限的方法是使用 MAXRECURION。

SQL 雙親節點查找所有子節點

相應地給出查找所有父節點的方法(擷取祖先 Ancestors,就是把 id 和 parentId 反過來)

SQLite ok 了,而 MySQL 呢?

至此,我們的目的可以說已經達到了,而且還不錯,因為這是不限層數的(以前 CMS 常說的“無限級”分類)。——其實,一般情況下,層數超過三層就很多,很複雜了,一般使用者如無特殊需求,也用不上這麼多層。于是,在給定層數的限制下,可以寫标準的 SQL 來完成該任務——盡管有點寫死的感覺~~

SQL 雙親節點查找所有子節點

但是生成的結果和第一個例子相比起來有點奇怪,而且不好給 Java 用,——那就再找找其他例子

這個總算像點樣子了,結果是這樣子的。

SQL 雙親節點查找所有子節點

相應地給出查找所有父節點的方法(擷取祖先 Ancestors,就是把 id 和 parentId 反過來, 還有改改 IN 裡面的字段名)

這樣就很通用啦~無論你 SQLite 還是 MySQL。

其他查詢:

查詢直接子節點的總數:

使用 with 語句遞歸,通俗易懂的例子(英文),我第一個成功的例子就是從這裡 copy 的,另外還可以查層數 level 和反向的父節點:https://www.valentina-db.com/dokuwiki/doku.php?id=valentina:articles:recursive_query

标準寫法的出處(英文):http://stackoverflow.com/questions/20215744/how-to-create-a-mysql-hierarchical-recursive-query

很好的總結貼(英文):http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/

SQlite with 語句用法中文翻譯(太晦澀,不懂鳥) http://blog.csdn.net/aflyeaglenku/article/details/50978986

利用閉包做的樹結構(書上說這個方法最好,但同時覺得也很進階,英文)http://charlesleifer.com/blog/querying-tree-structures-in-sqlite-using-python-and-the-transitive-closure-extension/