我怎样才能编写一个程序来获取i到j或像这样的树?

时间:2016-03-02 16:26:34

标签: sql sql-server algorithm tsql database-design

我有一张像

这样的表格
        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

我想创建一个返回给定用户的ij代后代的过程:

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更好的价值来代表" infinity"在SQL?
  • 我如何实施我所描述的程序?
  • 是否有更好的方法来设计数据库以简化程序?

1 个答案:

答案 0 :(得分: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个级别并且您将收到错误)。