分類樹:C2 IT產品 → C12 數碼產品 → C18 數碼攝像機
需求:給定 ClassID = 'C18',遞歸向上查找所有父節點,用 _ 拼接生成 Place = 'C2_C12_C18_'。
表結構:ClassTree_update(ClassID, ParentClassID, Place)
CREATE FUNCTION dbo.F_ContactAllParentPlaceByClassID(@classID VARCHAR(15))
RETURNS VARCHAR(500)
AS
BEGIN
DECLARE @ParentClassID VARCHAR(15), @Place VARCHAR(500);
SET @Place = '';
-- 獲取直接父節點
SELECT @ParentClassID = ParentClassID
FROM ClassTree_update
WHERE ClassID = @classID;
-- 遞歸終止條件:根節點 '000'
IF @ParentClassID <> '000'
BEGIN
-- 關鍵:先遞歸獲取上層路徑,再拼接當前父節點
SET @Place = dbo.F_ContactAllParentPlaceByClassID(@ParentClassID)
+ @ParentClassID + '_';
END
RETURN @Place;
END;
函數體內調用自身,創建時函數尚不存在會報錯。
解決:
ALTER FUNCTION 替換為完整遞歸邏輯-- 步驟 1:空殼
CREATE FUNCTION dbo.F_ContactAllParentPlaceByClassID(@classID VARCHAR(15))
RETURNS VARCHAR(500) AS BEGIN RETURN ''; END;
GO
-- 步驟 2:替換為真實遞歸邏輯
ALTER FUNCTION dbo.F_ContactAllParentPlaceByClassID(@classID VARCHAR(15))
RETURNS VARCHAR(500)
AS BEGIN
DECLARE @ParentClassID VARCHAR(15), @Place VARCHAR(500);
SET @Place = '';
SELECT @ParentClassID = ParentClassID FROM ClassTree_update WHERE ClassID = @classID;
IF @ParentClassID <> '000'
SET @Place = dbo.F_ContactAllParentPlaceByClassID(@ParentClassID) + @ParentClassID + '_';
RETURN @Place;
END;
GO
SELECT dbo.F_ContactAllParentPlaceByClassID('C18') AS Place;
-- 結果:C2_C12_C18_
結合新增記錄時的 @@IDENTITY 獲取新 ClassID,即可自動生成完整 Place。
| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| 遞歸函數 | 封裝性好、SQL 內部完成 | 深度遞歸性能差、默認最大 32 層 | 層級較淺(<10)、調用頻率低 |
| CTE 遞歸查詢 | 集合操作、無 32 層限制、可優化 | 需 SQL 2005+ | 現代版本首選 |
| 應用層遞歸 | 靈活、易調試、無數據庫限制 | 多次往返、需事務一致性自行保證 | 複雜邏輯、跨庫 |
| 物化路徑 | 查詢極快(Like 'C2_%') | 寫入維護成本高 | 讀多寫少、深度大 |
WITH RecursiveCTE AS (
SELECT ClassID, ParentClassID, CAST(ClassID + '_' AS VARCHAR(500)) AS Path
FROM ClassTree_update WHERE ClassID = 'C18'
UNION ALL
SELECT t.ClassID, t.ParentClassID, CAST(t.ClassID + '_' + r.Path AS VARCHAR(500))
FROM ClassTree_update t
JOIN RecursiveCTE r ON t.ClassID = r.ParentClassID
WHERE t.ParentClassID <> '000'
)
SELECT Path FROM RecursiveCTE WHERE ParentClassID = '000';