Sql 将递归限制到特定级别-重复行

Sql 将递归限制到特定级别-重复行,sql,sql-server,sql-server-2008,recursion,Sql,Sql Server,Sql Server 2008,Recursion,首先,这不是我的另一个同名问题的重复,我只是想不出一个更好的名字 我有一个名为Player的SQL表和另一个名为Unit的SQL表 每个球员必须通过外键UnitID属于一个团队 通过递归字段ParentUnitID,每个单元可以属于另一个团队 ParentUnit可以是ParentUnit(无限递归),但玩家只能属于一个团队 一个单位可能有很多孩子 所以它可能是(自上而下) TeamA(最高级别) 团队B(属于^^^) TeamC(属于^^) 团队D(属于^^) 玩家1(属于^^) 我的

首先,这不是我的另一个同名问题的重复,我只是想不出一个更好的名字

我有一个名为Player的SQL表和另一个名为Unit的SQL表

  • 每个球员必须通过外键UnitID属于一个团队
  • 通过递归字段ParentUnitID,每个单元可以属于另一个团队
  • ParentUnit可以是ParentUnit(无限递归),但玩家只能属于一个团队
  • 一个单位可能有很多孩子
所以它可能是(自上而下)

  • TeamA(最高级别)
  • 团队B(属于^^^)
  • TeamC(属于^^)
  • 团队D(属于^^)
  • 玩家1(属于^^)
我的问题是,如果我得到了一个球员的PlayerID(该表的PK),那么获得特定球队的最佳方式是什么

有关数据、结构和查询,请参见my SQLFiddle:

根据我的数据(来自小提琴),我希望能够让每个球员都在“TeamB”下,但是“Player4”的顶级单位应该是“TeamC”。为了做到这一点,我只想传入PlayerID和“TeamB”的ID。所以我的意思是“把所有球员和顶级单位都放在B队下面,然后过滤掉除Player4之外的所有球员

编辑:我认为上面的段落应该是:根据我的数据(来自小提琴),我希望能够建立在“TeamB”之下的顶级球队。对于“TeamB”之下的每个顶级球队“然后我想确定所有为该队效力或低于该队的球员。然后,我希望能够将玩家列表限制为一个或多个特定玩家。

正如您在SQLFiddle中看到的,我为每个玩家返回了多行,我确信这是一个快速修复,但我无法理解。。。这就是我在小提琴中想要的,但它,嗯,呃,有点复杂…:)

编辑:更多信息:

假设这是一个存储过程

  • 我通过了PlayerID 1,2,3,4
  • 我通过UnitID 2
我希望返回的数据看起来像

Player3, TeamC
仅返回Player3,因为它是TeamB(ID 2)的唯一后代,而TeamC是返回的,因为它是Player3所属的最高级别单位(UnitID 2以下)

如果我通过了:

  • 我通过了PlayerID 1,2,3,4
  • 我通过UnitID 6
我希望

Player1, Team2
Player2, Team2
这个答案已被完全改写。原始版本并非在所有情况下都有效

我必须更改CTE,以将每个单元的完整单元层次结构表示为一个可能的根(顶部单元)。它允许一个真正的层次结构,每个单元有多个子级

我扩展了其中的示例数据,让玩家同时被分配到第11单元和第12单元。它正确地返回了3个玩家的正确行,这些玩家在1号单位下的某个级别上为一个单位比赛

“根”单元ID和玩家ID列表位于底部最外层的WHERE子句中,便于根据需要更改ID

with UnitCTE as (
  select u.UnitID,
         u.Designation UnitDesignation,
         u.ParentUnitID as ParentUnitID,
         p.Designation as ParentUnitDesignation,
         u.UnitID TopUnitID,
         u.Designation TopUnitDesignation,
         1 as TeamLevel
    from Unit u
    left outer join Unit p
      on u.ParentUnitId = p.UnitID
  union all
  select t.UnitID,
         t.Designation UnitDesignation,
         c.UnitID as ParentUnitID,
         c.UnitDesignation as ParentUnitDesignation,
         c.TopUnitID,
         c.TopUnitDesignation,
         TeamLevel+1 as TeamLevel
    from Unit t
    join UnitCTE c
      on t.ParentUnitID = c.UnitID
)
select p.PlayerID,
       p.Designation,
       t1.*
  from UnitCTE t1
  join UnitCTE t2
    on t2.TopUnitID = t1.UnitID
   and t2.TopUnitID = t1.TopUnitID
  join Player p
    on p.UnitID = t2.UnitID
 where t1.ParentUnitID = 1
   and playerID in (1,2,3,4,5,6)
这是一个稍微优化的版本,在CTE中嵌入了机组ID标准。CTE仅计算以单元为根的层次结构,其中父ID是所选的单元ID(本例中为1)



这是我的原始答案。只有当单元层次结构被限制为每个单元只允许一个子单元时,它才起作用。问题中的SQL Fiddle示例在第1单元中有3个子元素,因此如果在第1单元中运行,它会错误地为玩家3、5和6返回多行

下面是一个例子来说明这个问题

这个答案已被完全改写。原始版本并非在所有情况下都有效

我必须更改CTE,以将每个单元的完整单元层次结构表示为一个可能的根(顶部单元)。它允许一个真正的层次结构,每个单元有多个子级

我扩展了其中的示例数据,让玩家同时被分配到第11单元和第12单元。它正确地返回了3个玩家的正确行,这些玩家在1号单位下的某个级别上为一个单位比赛

“根”单元ID和玩家ID列表位于底部最外层的WHERE子句中,便于根据需要更改ID

with UnitCTE as (
  select u.UnitID,
         u.Designation UnitDesignation,
         u.ParentUnitID as ParentUnitID,
         p.Designation as ParentUnitDesignation,
         u.UnitID TopUnitID,
         u.Designation TopUnitDesignation,
         1 as TeamLevel
    from Unit u
    left outer join Unit p
      on u.ParentUnitId = p.UnitID
  union all
  select t.UnitID,
         t.Designation UnitDesignation,
         c.UnitID as ParentUnitID,
         c.UnitDesignation as ParentUnitDesignation,
         c.TopUnitID,
         c.TopUnitDesignation,
         TeamLevel+1 as TeamLevel
    from Unit t
    join UnitCTE c
      on t.ParentUnitID = c.UnitID
)
select p.PlayerID,
       p.Designation,
       t1.*
  from UnitCTE t1
  join UnitCTE t2
    on t2.TopUnitID = t1.UnitID
   and t2.TopUnitID = t1.TopUnitID
  join Player p
    on p.UnitID = t2.UnitID
 where t1.ParentUnitID = 1
   and playerID in (1,2,3,4,5,6)
这是一个稍微优化的版本,在CTE中嵌入了机组ID标准。CTE仅计算以单元为根的层次结构,其中父ID是所选的单元ID(本例中为1)



这是我的原始答案。只有当单元层次结构被限制为每个单元只允许一个子单元时,它才起作用。问题中的SQL Fiddle示例在第1单元中有3个子元素,因此如果在第1单元中运行,它会错误地为玩家3、5和6返回多行

下面是一个例子来说明这个问题


这就是你想要的:Vijay,把你想要的结果作为一个表格!你的问题毫无意义。你说“我想让每个玩家都在“TeamB”下,但是“Player4”的顶级单位应该是“TeamC”。为了做到这一点,我只想传入PlayerID和“TeamB”的ID。所以我说“让所有玩家和顶级单位在TeamB下,然后过滤掉除Player4之外的所有玩家。“对不起,这毫无意义,与SQLFiddle的数据和结果完全不符。@SteveKass-我在问题中添加了更多信息,说它是胡说八道似乎有点苛刻,但我能理解我在哪里失去了你!希望我的编辑更有意义,如果没有,请解释具体哪一部分(希望不是全部!)没有意义,我会尽力进一步解释。这就是你想要的:Vijay,以表格形式给出你想要的结果!你的问题毫无意义。你说“我想让每个球员都在“TeamB”下,但是“Player4”的顶级单位应该是“TeamC”。为了做到这一点,我只想传递“TeamB”的玩家ID和ID。所以我说“让所有球员和顶级单位在TeamB下”
with UnitCTE as (
  select u.UnitID,
         u.Designation UnitDesignation,
         u.ParentUnitID as ParentUnitID,
         p.Designation as ParentUnitDesignation,
         u.UnitID TopUnitID,
         u.Designation TopUnitDesignation,
         1 as TeamLevel
    from Unit u
    left outer join Unit p
      on u.ParentUnitId = p.UnitID
   where u.ParentUnitID = 1
  union all
  select t.UnitID,
         t.Designation UnitDesignation,
         c.UnitID as ParentUnitID,
         c.UnitDesignation as ParentUnitDesignation,
         c.TopUnitID,
         c.TopUnitDesignation,
         TeamLevel+1 as TeamLevel
    from Unit t
    join UnitCTE c
      on t.ParentUnitID = c.UnitID
)
select p.PlayerID,
       p.Designation,
       t1.*
  from UnitCTE t1
  join UnitCTE t2
    on t2.TopUnitID = t1.UnitID
  join Player p
    on p.UnitID = t2.UnitID
 where playerID in (1,2,3,4,5,6)
with UnitCTE as
  select UnitID,
         Designation UnitDesignation,
         ParentUnitID as ParentUnitID,
         cast(null as varchar(50)) as ParentUnitDesignation,
         UnitID TopUnitID,
         Designation TopUnitDesignation,
         1 as TeamLevel
    from Unit
   where ParentUnitID is null
  union all
  select t.UnitID,
         t.Designation UnitDesignation,
         c.UnitID,
         c.UnitDesignation,
         c.TopUnitID,
         c.TopUnitDesignation,
         TeamLevel+1 as TeamLevel
    from Unit t
    join UnitCTE c
      on t.ParentUnitID = c.UnitID
)
select p.PlayerID,
       p.Designation,
       t2.*
  from Player p
  join UnitCTE t1
    on p.UnitID = t1.UnitID
  join UnitCTE t2
    on t2.TopUnitID = t1.TopUnitID
   and t1.TeamLevel >= t2.TeamLevel
  join UnitCTE t3
    on t3.TopUnitID = t1.TopUnitID
   and t2.TeamLevel = t3.TeamLevel+1
 where t3.UnitID = 2
   and playerID in (1,2,3,4)