【问题标题】:SQL Server function to get top level parent in hierarchySQL Server 函数获取层次结构中的顶级父级
【发布时间】:2015-07-28 23:47:41
【问题描述】:

我有下表(master_group)结构:

code    name                      under 

1       National Sales Manager    1
2       regional sales manager    1 
3       area sales manager        2 
4       sales manager             3

如何获得特定行的最终父级,例如:

code    name                      under     ultimateparent

1       National Sales Manager    1         1
2       regional sales manager    1         1
3       area sales manager        2         1
4       sales manager             3         1

【问题讨论】:

  • @MartinDavidValentinoSiagian :我在问题中提到了第二个表结构。

标签: sql sql-server sql-server-2012


【解决方案1】:

递归 cte 从顶部到子级:

with cte as(
  select *, code as ultimate from t where code = under
  union all
  select t.*, c.ultimate from t
  join cte c on c.code = t.under
  where t.code <> t.under
)
select * from cte

对于数据:

create table t (code int, name varchar(100), under int)
insert into t values
(1, 'National Sales Manager', 1),
(2, 'regional sales manager', 1),
(3, 'area sales manager', 2),
(4, 'sales manager', 3),
(5, 'a', 5),
(6, 'b', 5),
(7, 'c', 5),
(8, 'd', 7),
(9, 'e', 7),
(10, 'f', 9),
(11, 'g', 9)

它生成输出:

code    name                    under   ultimate
1       National Sales Manager  1       1
5       a                       5       5
6       b                       5       5
7       c                       5       5
8       d                       7       5
9       e                       7       5
10      f                       9       5
11      g                       9       5
2       regional sales manager  1       1
3       area sales manager      2       1
4       sales manager           3       1

小提琴http://sqlfiddle.com/#!6/17c12e/1

【讨论】:

  • 请看一下这个小提琴:sqlfiddle.com/#!6/f9981e/2/0。我改变了一点数据。我有 44 条记录,但它只显示 39 条。我找不到问题所在。
  • @NitinKabra 最后你有很长很长的 ID,它们不是任何其他行的父母或孩子。
【解决方案2】:

您可以使用递归 CTE 遍历树,然后为每个代码选择最高级别:

with cte as (
      select mg.code, mg.name as name, mg.under as under, mg.under as parent, 1 as lev
      from master_group mg
      union all
      select mg.code, mg.name, mg.under, cte.under as parent, cte.lev + 1
      from master_group mg join
           cte
           on mg.under = cte.code
      where cte.under is not null and cte.under <> mg.code
     )
select code, name, under, parent as ultimateparent
from (select cte.*, max(lev) over (partition by cte.code) as maxlev
      from cte
     ) t
where lev = maxlev;

Here 是一个 SQL Fiddle。

【讨论】:

    【解决方案3】:

    当它是最高记录时,我会将 NULL 放在下面(在我的示例 ParentId 中)。有了这个假设,这里有一个解决方案

    ;

    WITH Result AS
    (
        SELECT Id, ParentId, Name, Id as [Top] FROM
        sample
        where  ParentId IS NULL 
        UNION ALL 
        SELECT s.Id, s.ParentId, s.Name, [Top]
        FROM sample s INNER JOIN Result R ON s.ParentId = R.Id
    )
    

    http://sqlfiddle.com/#!6/13b9d/14

    【讨论】:

      【解决方案4】:

      我建议你使用这样的递归函数:

      CREATE FUNCTION dbo.parentID (@code int)
      RETURNS int AS
      BEGIN
          DECLARE @ResultVar int
          SELECT @ResultVar = (SELECT under FROM master_group WHERE code = @code)
          IF @ResultVar <> @code 
          BEGIN
              SELECT @ResultVar = dbo.parentID(@ResultVar)
          END
          RETURN @ResultVar
      END
      GO
      

      这样使用它:

      SELECT *, 
             dbo.parentId(code) AS ultimateparent
      FROM master_group
      

      【讨论】:

        【解决方案5】:

        我将无耻地从另一个答案中窃取数据设置,并演示您如何使用 hierarchyid 做到这一点:

        create table t (code int, name varchar(100), under int)
        insert into t values
            (1, 'National Sales Manager', null),
            (2, 'regional sales manager', 1),
            (3, 'area sales manager', 2),
            (4, 'sales manager', 3),
            (5, 'a', null),
            (6, 'b', 5),
            (7, 'c', 5),
            (8, 'd', 7),
            (9, 'e', 7),
            (10, 'f', 9),
            (11, 'g', 9);
        
        with cte as (
            select code, name, under as parentCode, code as ultimateParent, cast('/' + cast(code as varchar) + '/' as nvarchar(max)) as h
            from t
            where under is null
        
            union all
        
            select child.code, child.name, child.under as ParentCode, parent.ultimateParentCode, cast(parent.h + cast(child.code as varchar) + '/' as nvarchar(max))
            from t as child
            join cte as parent
                on child.under = parent.code
        ), hier as (
        select code, name, parentCode, ultimateParentCode, cast(h as hierarchyid) as h
        from cte
        )
        select code, name, parentCode, ultimateParentCode, h.ToString(), h.GetAncestor(h.GetLevel()-1).ToString()
        from hier
        

        请记住,递归 CTE 只需执行一次(或在数据更改时)。我要说的一点是,一旦你计算了一个hierarchyid(你可以将它存储在行中,顺便说一句),很容易回答你提出的问题,方法是调用hierarchyid(如果你想要的话,可能还有一个join取回祖先的信息)。

        【讨论】:

          猜你喜欢
          • 2012-03-01
          • 2021-07-05
          • 1970-01-01
          • 2023-03-31
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-03-13
          • 1970-01-01
          相关资源
          最近更新 更多