SQL Server 2008中的递归同表查询

14
在 SQL Server 2008 数据库中,我有以下表格:
Id  Name       ParentFolder
--  ----       ------------
1   Europe     NULL
2   Asia       NULL
3   Germany    1
4   UK         1
5   China      2
6   India      2
7   Scotland   4

ParentFolder是对同一表中Id的外键。我想创建一个视图,使其结果类似于以下内容:

Id  Name       FullName
--  ----       --------
1   Europe     Europe
2   Asia       Asia
3   Germany    Europe/Germany
4   UK         Europe/UK
5   China      Asia/China
6   India      Asia/India
7   Scotland   Europe/UK/Scotland

正如您所看到的,我需要通过递归使用ParentFolder关系任意次数来构建FullName值,直到找到NULL为止。

编辑。表中的每一行都知道其他行是其父级,但不知道它在层次结构中的绝对位置。因此,一个存储每个行的绝对位置的谱系系统不适用于此。

我知道SQL Server 2008的hierarchyid功能,但据我所知,它仅适用于固定数量的递归级别。然而,在我的情况下,您永远不知道会找到多少级别,并且它们可能会从一行到另一行发生变化。

我也看到过类似的问题发布在这里。但是,我认为没有人问过如何为表中的每一行构建“路径”。如果我错过了,请原谅。

非常感谢。

4个回答

35

试试这个:

    DECLARE @tbl TABLE (
         Id INT
        ,[Name] VARCHAR(20)
        ,ParentId INT
        )

    INSERT INTO @tbl( Id, Name, ParentId )
    VALUES
     (1, 'Europe', NULL)
    ,(2, 'Asia',   NULL)
    ,(3, 'Germany', 1)
    ,(4, 'UK',      1)
    ,(5, 'China',   2)
    ,(6, 'India',   2)
    ,(7, 'Scotland', 4)
    ,(8, 'Edinburgh', 7)
    ,(9, 'Leith', 8)

    ;
WITH  abcd
        AS (
              -- anchor
            SELECT  id, [Name], ParentID,
                    CAST(([Name]) AS VARCHAR(1000)) AS "Path"
            FROM    @tbl
            WHERE   ParentId IS NULL
            UNION ALL
              --recursive member
            SELECT  t.id, t.[Name], t.ParentID,
                    CAST((a.path + '/' + t.Name) AS VARCHAR(1000)) AS "Path"
            FROM    @tbl AS t
                    JOIN abcd AS a
                      ON t.ParentId = a.id
           )
SELECT * FROM abcd

2

谢谢,约翰。我不确定这会起作用,因为Lineage列意味着表中的每一行都必须“知道”其在层次结构中的绝对位置。我在原帖中没有说过这个(现在正在编辑以澄清),但我宁愿避免使用这种绝对定位系统。 :-) - CesarGon

2
听起来您应该查看Sql Server的CLR支持
CLR集成意味着您现在可以使用任何.NET Framework语言(包括Microsoft Visual Basic .NET和Microsoft Visual C#)编写存储过程、触发器、用户定义类型、用户定义函数(标量和表值),以及用户定义聚合函数。

谢谢 Chad。所以,你的意思是我需要在C#(或VB.NET)中创建一个表值函数,并将其用于计算视图中的FullName列,对吗? - CesarGon
@CesarGon:是的。我认为用C#作为CLR SP或函数编写解决方案比我为此而创造的TSQL混乱要容易得多。我还认为这将比TSQL更有效(因为与游标相关的锁/闩)。祝好运。 - Chad
1
你真的不需要编写CLR函数,看一下Damir Sudarevic在下面的回答。 "常用表达式"解决了大多数分层数据问题。 http://msdn.microsoft.com/en-us/library/ms186243.aspx - Mehmet Ergut

1
我尝试了上面的解决方案,但发现它只对我有效到2个级别。(也许我没有理解或错过了什么。)
为了获得完全限定路径的解决方案,我成功地使用了这个自定义函数:
CREATE FUNCTION GetFQN(@recid int)
RETURNS VARCHAR(1000)

AS

BEGIN
    DECLARE @path AS VARCHAR(1000)
    DECLARE @parent_recid AS INT

    SET @path           =   (SELECT BranchName FROM Branches WHERE Recid = @recid)
    SET @parent_recid   =   (SELECT recid_parent FROM Branches WHERE Recid = @recid)


    WHILE @parent_recid != -1
    BEGIN
        SET @path = (SELECT BranchName FROM Branches WHERE recid = @parent_recid) + '/' + @path 
        SET @parent_recid = (SELECT recid_parent FROM Branches WHERE recid = @parent_recid)
    END

    RETURN (@Path)
END

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接