【问题标题】:Recursive same-table query in SQL Server 2008SQL Server 2008 中的递归同表查询
【发布时间】:2010-12-17 23:03:15
【问题描述】:

我在 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 的 FK。我想创建一个视图,结果如下:

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 功能,但据我所知,它仅适用于固定数量的递归级别。然而,就我而言,你永远不知道会找到多少层,而且它们可能会逐行变化。

我也在这里看到了与此类似的问题。但是,我认为没有人询问为表格中的每一行构建“路径”。如果我错过了,对不起。

非常感谢。

【问题讨论】:

    标签: sql-server-2008 recursion


    【解决方案1】:

    我不确定这是否适用于你的情况,但在这个例子中 http://www.pure-performance.com/2009/03/managing-hierarchical-data-in-sql/ 有一些关于使用额外的列,称为 lineage。

    我已经成功使用了这个方法。

    【讨论】:

    • 谢谢,约翰。我不确定这是否可行,因为 Lineage 列意味着表中的每一行都必须“知道”它在层次结构中的绝对位置。我在原帖中没有说(现在编辑以澄清),但我宁愿避免使用这种绝对定位系统。 :-)
    【解决方案2】:

    听起来您应该为 Sql Sever 结帐 CLR support。

    CLR 集成意味着您现在可以使用任何 .NET Framework 语言编写存储过程、触发器、用户定义类型、用户定义函数(标量和表值)和用户定义聚合函数,包括Microsoft Visual Basic .NET 和 Microsoft Visual C#。

    【讨论】:

    • 谢谢乍得。所以,基本上,您是说我需要在 C#(或 VB.NET)中创建一个表值函数并使用它来计算视图中的 FullName 列,对吧?
    • @CesarGon:是的。我认为该解决方案将比我为此提出的 TSQL 的 gooble 更容易用 C# 编写为 CLR SP 或函数。我也认为这将比 TSQL 更有效(因为与游标相关联的 LOCKS/LATCHES)。祝你好运
    • 你真的不需要写CLR函数,看看下面Damir Sudarevic的回答。 “通用表表达式”解决了大部分分层数据问题。 msdn.microsoft.com/en-us/library/ms186243.aspx
    【解决方案3】:

    试试这个:

        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
    

    【讨论】:

      【解决方案4】:

      我尝试了上面的解决方案,但发现这只对我有效 2 个级别。 (也许我没有理解或错过了什么。)

      为了获得 m 解决方案的完全限定路径,我已成功使用此自定义函数:

      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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-07-13
        • 1970-01-01
        • 2014-12-19
        • 2012-12-05
        • 1970-01-01
        • 2017-02-02
        相关资源
        最近更新 更多