将递归限制到某个级别 - 重复行

时间:2012-07-16 21:25:38

标签: sql sql-server sql-server-2008 recursion

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

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

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

所以它可能是(自上而下)......

  • TeamA(是最高级别)
  • TeamB(属于^^)
  • TeamC(属于^^)
  • TeamD(属于^^)
  • Player_1(属于^^)

我的问题是,如果我获得了玩家的PlayerID(该表的PK),那么获得特定团队的最佳方式是什么?

请参阅我的SQLFiddle获取数据,结构和查询:http://sqlfiddle.com/#!3/78965/3

凭借我的数据(来自小提琴),我希望能够让每个玩家都获得“TeamB”,但是“Player4”的顶级单位应该是“TeamC”。为了做到这一点,我要传递的是PlayerID和“TeamB”的ID。所以我说“让所有玩家和顶级单位在TeamB下,然后过滤掉所有玩家除了玩家4。”

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

正如你在SQLFiddle中看到的那样,我为每个玩家找回了多行,我确信它是一个快速解决方案,但我无法弄明白......这就是我在小提琴中的目的,但是它,嗯,有点繁琐...... :)。

编辑:更多信息:

好的,所以如果想象这是一个存储过程。

  • 我传入PlayerIDs 1,2,3,4
  • 我通过了UnitID 2

我希望返回的数据看起来像

Player3, TeamC

只返回Player3,因为它是唯一一个作为TeamB(ID 2)后代的玩家,并返回TeamC,因为它是Player3所属的最高级别单元(低于UnitID 2)。

如果我改为通过:

  • 我传入PlayerIDs 1,2,3,4
  • 我通过了UnitID 6

我希望

Player1, Team2
Player2, Team2

2 个答案:

答案 0 :(得分:2)

with UnitCTE as (
  select UnitID,
         Designation,
         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,
         c.UnitID,
         c.Designation,
         c.TopUnitID,
         c.TopUnitDesignation,
         TeamLevel+1 as TeamLevel
    from Unit t
    join UnitCTE c
      on t.ParentUnitID = c.UnitID
      --WHERE t.UnitID = 1
),
x AS (
select Player.PlayerID,
       pDesignation = Player.Designation, t1.*,
       rn = ROW_NUMBER() OVER (PARTITION BY Player.PlayerID ORDER BY Player.Designation)
  from Player
  join UnitCTE t1
    on Player.UnitID = t1.UnitID
  join UnitCTE t2
    on t1.TopUnitID = t2.TopUnitID
   and t2.TeamLevel=2
)
SELECT * FROM x
WHERE rn = 1
   ORDER BY TeamLevel

答案 1 :(得分:2)

此答案已完全重写。原件在所有情况下都不起作用

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

我扩展了此SQL Fiddle中的示例数据,以便将一个玩家分配给单元11和12.它正确地为单元1下某个级别的单元播放的3个玩家中的每个玩家返回正确的行。

“root”单元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)

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)



以下是我的原始答案。 只有在单位层次结构被限制为每个单元只允许一个子节点时才有效。问题中的SQL Fiddle示例有3个子单元用于单元1,因此如果针对单元1运行,则错误地为玩家3,5和6返回多行

以下是展示问题的SQL Fiddle

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)