2016-03-02 24 views
5

我有這樣如何編寫一個通過j或這樣的樹來獲取關卡的程序?

 Users 
------------------------- 
id | ancestor_id | .... 
------------------------- 
1 | NULL  | .... 
2 |  1  | .... 
3 |  1  | .... 
4 |  3  | .... 
5 |  3  | .... 

一個表,將代表像

level 1   1 
       /\ 
    level 2  2 3 
        /\ 
    level 3   4 5 

一棵樹,我想創建一個返回通過j第四代給定用戶的後裔i個程序:

CREATE PROCEDURE DescendantsLevel 
    @user_id INT, 
    @i INT, 
    @j INT 
AS 
    .... 

如果@jNULL,但是,它返回所有後代從代號@i開始。

例子:

EXEC DescendantLevel @user_id=1,@i=2,@j=NULL 

將返回

------------------------- 
id | ancestor_id | .... 
------------------------- 
1 | NULL  | .... 
2 |  1  | .... 
3 |  1  | .... 
4 |  3  | .... 
5 |  3  | .... 

EXEC DescendantLevel @user_id=1,@i=1,@j=2 

將返回

 Users 
------------------------- 
id | ancestor_id | .... 
------------------------- 
1 | NULL  | .... 
2 |  1  | .... 
3 |  1  | .... 

幾個問題,我有:

  • 是否有更好的價值比NULL來表示SQL「無限」的概念,一些?
  • 如何實現我描述的過程?
  • 有沒有更好的設計數據庫的方法來簡化程序?
+2

查找遞歸類。如果你使用這種設計,這將是解決這個問題的最簡單的方法。您也可以將嵌套集模型視爲鄰接列表的更好替代方案。 –

+0

如果在你的第一個例子中'@ i'是2,那麼爲什麼返回id#1? –

+0

爲什麼不添加關卡列?使查詢非常簡單。 – maraca

回答

2

使用遞歸CTE:

DECLARE @test TABLE (id INT NOT NULL, ancestor_id INT NULL) 

DECLARE 
    @id INT = 1, 
    @i INT = 1, 
    @j INT = 2 

INSERT INTO @test (id, ancestor_id) 
VALUES 
    (1, NULL), 
    (2, 1), 
    (3, 1), 
    (4, 3), 
    (5, 3) 

;WITH CTE_Tree AS 
(
    SELECT 
     id, 
     ancestor_id, 
     1 AS lvl, 
     id AS base 
    FROM 
     @test 
    WHERE 
     id = @id 
    UNION ALL 
    SELECT 
     C.id, 
     C.ancestor_id, 
     P.lvl + 1 AS lvl, 
     P.base AS base 
    FROM 
     CTE_Tree P 
    INNER JOIN @test C ON C.ancestor_id = P.id 
    WHERE 
     lvl <= COALESCE(@j, 9999) 
) 
SELECT 
    id, 
    ancestor_id 
FROM 
    CTE_Tree 
WHERE 
    lvl BETWEEN @i AND COALESCE(@j, 9999) 

這依賴於不超過9999個水平遞歸(實際上是在遞歸SQL Server的默認限制是100,所以100個多層次,你會得到一個錯誤)。

相關問題