SQL 也能遞歸?一文搞懂 Recursive CTE的魔力
很多人以為遞歸(Recursive)只屬于編程語言,和 SQL 沒什么關系。但其實 SQL 中也能實現(xiàn)遞歸操作,特別是在處理樹結構、路徑查找時,WITH RECURSIVE 展現(xiàn)出強大威力。本文將帶你一步步掌握 SQL 中的遞歸查詢,揭開 Recursive CTE 的神秘面紗!
Recursive CTE(遞歸公共表表達式)
在 SQL 中,遞歸公共表表達式(Recursive CTE) 是一種強大的查詢手段。通過 WITH RECURSIVE 語法,開發(fā)者可以定義一個可以引用自身的查詢結構,實現(xiàn)在查詢過程中“自我迭代”的效果。
簡單來說,SQL 也能“遞歸”。
不過需要注意的是,遞歸查詢必須設計得當,確保它在某個條件下能夠終止。否則,就可能陷入“無限循環(huán)”,導致查詢無法完成,甚至拖垮數(shù)據(jù)庫性能。
那么,SQL 的遞歸到底怎么寫?能解決哪些實際問題?接下來,我們就從原理、寫法,到典型應用場景,一步步帶你搞懂 Recursive CTE 的魔力。
來看一個最簡單的例子,生成從 1 到 5 的數(shù)字序列:
圖片
我們來拆解一下這段 SQL 是如何“遞歸”的:
- 首先,SELECT 1 AS num 是 遞歸的起點,稱為錨點(Anchor Member),遞歸從這里開始。
- 接下來,SELECT num + 1 FROM rec WHERE num < 5 是 遞歸部分,它會反復執(zhí)行,直到 num < 5 不再滿足為止。
- UNION ALL 將錨點和遞歸部分的結果組合起來。
整個查詢的執(zhí)行過程大致如下:
- 第一步,輸出 1;
- 然后執(zhí)行遞歸部分,1 + 1 = 2,滿足條件,繼續(xù);
- 依次得到 3、4、5;
- 當 num 增加到 6 時,不滿足 num < 5,遞歸終止。
把遞歸邏輯“套”起來,這次不是 1 到 5,而是 100、200、300……直到 700。核心邏輯沒變,只是換了組數(shù)字而已。
圖片
示例:斐波那契數(shù)列(Fibonacci Sequence)
WITH RECURSIVE 不僅可以用于構造數(shù)字序列,還可以實現(xiàn)更復雜的遞歸計算。比如,我們可以利用它來生成前 8 個斐波那契數(shù):
圖片
示例:樹結構遍歷(Tree Traversal)
除了計算數(shù)值,WITH RECURSIVE 還可以用于遍歷樹形結構,這在處理層級數(shù)據(jù)(如組織架構、分類標簽、菜單結構等)時非常常見。
比如,下面是一個“標簽(tags)層級結構”的遞歸遍歷案例:
圖片
圖片
圖片
示例:圖遍歷(Graph Traversal)
借助 WITH RECURSIVE,我們甚至可以在 SQL 中實現(xiàn)任意圖結構的遍歷(Graph Traversal)。這對于表示如路線網(wǎng)絡、依賴關系圖、社交圖譜等復雜結構非常有用。
不過需要特別注意的是:如果圖中存在環(huán)(cycle),就必須進行循環(huán)檢測,否則遞歸查詢可能會陷入死循環(huán),永遠無法終止。
一種常見的做法是:在遞歸過程中記錄當前路徑,每次延伸路徑前,先檢查目標節(jié)點是否已訪問過,從而避免重復走回頭路。下面的示例中詳細演示這一做法。
圖片
圖片
需要注意的是,這類圖結構中可能包含有向環(huán)(directed cycles),比如節(jié)點 1、5 和 8 之間就形成了一個閉環(huán)。
枚舉從某個節(jié)點出發(fā)的所有路徑(Enumerate All Paths from a Node)
下面這個查詢展示了如何使用 WITH RECURSIVE 來枚舉從節(jié)點 1 出發(fā)的所有路徑:
圖片
需要注意的是,這個查詢的結果并不限于最短路徑。
例如,對于節(jié)點 5,結果中既包含直接路徑 [1, 5],也包含更長的路徑 [1, 3, 5]。
換句話說,它會列出所有可能走通的路徑,而不是只保留最短的那一條。如果你希望過濾最短路徑或添加路徑權重,還需要進一步處理。
圖片
枚舉兩個節(jié)點之間的無權最短路徑(Enumerate Unweighted Shortest Paths)
WITH RECURSIVE 還可以用來查找兩個節(jié)點之間的所有無權最短路徑。為了保證遞歸查詢在到達目標節(jié)點后及時終止,我們可以借助窗口函數(shù),檢查當前新增節(jié)點中是否已包含目標節(jié)點。
下面的查詢展示了如何找出從節(jié)點 1(起點)到節(jié)點 8(終點)之間的所有無權最短路徑:
圖片
遞歸不僅屬于編程語言,SQL 也能“遞歸”!借助 WITH RECURSIVE,我們可以優(yōu)雅地處理數(shù)字序列、樹結構、圖遍歷等復雜問題。無論是層級查詢,還是路徑搜索,Recursive CTE 都是一種強大且靈活的利器。