将递归限制到某个级别

时间:2012-07-16 18:36:42

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

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

  • 每个玩家必须通过外键TeamID属于一个团队。
  • 每个团队都可以通过递归字段ParentTeamID属于另一个团队。

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

  • TeamA
  • TeamB
  • Team76
  • 组8
  • Player_ME

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

到目前为止我的查询(获得所有团队):

WITH TeamTree
    AS (
        SELECT ParentTeam.*, Player.PlayerID, 0 as Level
        FROM Team ParentTeam
        INNER JOIN Player ON Player.TeamID = ParentTeam.TeamID
        WHERE Player.PlayerID IN (SELECT * FROM dbo.Split(@PlayerIDs,','))
        UNION ALL 
        SELECT ChildTeam.*, TeamTree.PlayerID AS PlayerID, TeamTree.Level + 1
        FROM Team ChildTeam
                INNER JOIN TeamTree TeamTree
        ON ChildTeam.TeamID = TeamTree.ParentTeamID
    )

现在虽然我认为这是一个合适的起点,但我认为可能有更好的方法。另外,我有点卡住了!我尝试在连接中使用L​​evel(在子查询中),但它没有工作。

关于如何在树上工作并获得最高级别详细信息的任何想法?

编辑:

ParentTeam可以是ParentTeam(无限递归),但玩家只能属于一个团队。

数据结构 球队: TeamID(PK),Name,ParentTeamID(递归字段)

播放器: PlayerID(PK),名称,TeamID(FK)

示例数据:

Team:
1, TeamA, NULL
2, TeamB, 1
3, Team76, 2
4, Group8, 3

Player:
1, Player_ME, 4
2, Player_TWO, 2

因此,根据上述数据,两位玩家都应该(在查询中)显示他们有一个" TopLevelTeam" TeamA

2 个答案:

答案 0 :(得分:3)

我相信这就是你要找的东西,免费提供一些额外的信息:-) 安德鲁在他的编辑版本中有正确的想法,但我认为他的实施是不正确的。

架构和查询位于SQL Fiddle

with teamCTE as (
  select TeamID,
         TeamName,
         cast(null as int) as ParentTeamID,
         cast(null as varchar(10)) as ParentTeamName,
         TeamID TopTeamID,
         TeamName TopTeamName,
         1 as TeamLevel
    from team
   where ParentTeamID is null
  union all
  select t.TeamID,
         t.TeamName,
         c.TeamID,
         c.TeamName,
         c.TopTeamID,
         c.TopTeamName,
         TeamLevel+1 as TeamLevel
    from team t
    join teamCTE c
      on t.ParentTeamID = c.TeamID
)
select p.PlayerID,
       p.PlayerName,
       t.*
  from player p
  join teamCTE t
    on p.TeamID = t.TeamID

编辑 - 回答评论中的问题

只需第二次加入CTE,即可导航到玩家团队层次结构中的任何级别。在您的情况下,您要求排名第二的团队:SQL Fiddle

with teamCTE as (
  select TeamID,
         TeamName,
         cast(null as int) as ParentTeamID,
         cast(null as varchar(10)) as ParentTeamName,
         TeamID TopTeamID,
         TeamName TopTeamName,
         1 as TeamLevel
    from team
   where ParentTeamID is null
  union all
  select t.TeamID,
         t.TeamName,
         c.TeamID,
         c.TeamName,
         c.TopTeamID,
         c.TopTeamName,
         TeamLevel+1 as TeamLevel
    from team t
    join teamCTE c
      on t.ParentTeamID = c.TeamID
)
select p.PlayerID,
       p.PlayerName,
       t1.*,
       t2.TeamID Level2TeamID,
       t2.TeamName Level2TeamName
  from player p
  join teamCTE t1
    on p.TeamID = t1.TeamID
  join teamCTE t2
    on t1.TopTeamID = t2.TopTeamID
   and t2.TeamLevel=2

答案 1 :(得分:0)

WITH TeamTree
    AS (
        SELECT ParentTeam.*, Player.PlayerID AS UrPlayerID, 0 as Level
        FROM Team ParentTeam
        INNER JOIN Player ON Player.TeamID = ParentTeam.TeamID
        WHERE Player.PlayerID IN (SELECT * FROM dbo.Split(@PlayerIDs,','))
        UNION ALL 
        SELECT ChildTeam.*, TeamTree.PlayerID AS PlayerID, TeamTree.Level + 1
        FROM Team ChildTeam
                INNER JOIN TeamTree TeamTree
        ON ChildTeam.ParentTeamID = TeamTree.TeamID /* These were reversed, I think */
           AND UrPlayerID=ChildTeam.PlayerID /* ADDED */
    )

否则你会得到一个巨大的重复行,比如玩家数量的平方,不是吗?

- (以下评论后) 非常正确,我误读了架构。看,你不需要把玩家带到最后。我认为团队树的安排可能因玩家而有所不同,但事实并非如此。所以

WITH recursive TeamTree AS (
   SELECT TeamID, ParentTeamID FROM Team T1
   UNION ALL
   SELECT T1.TeamID, T2.ParentTeamID FROM T1 JOIN T2 ON T1.ParentTeamID=T2.TeamID
   )
 SELECT TeamTree.* FROM TeamTree JOIN Team T3 
      ON TeamTree.ParentTeamID=T3.TeamID WHERE T3.ParentTeamID IS NULL;

这将为您提供每个团队及其根祖先的表格。现在将其加入播放器表。

SELECT * FROM Player JOIN  (WITH TeamTree AS (
       SELECT TeamID, ParentTeamID FROM Team T1
       UNION ALL
       SELECT T1.TeamID, T2.ParentTeamID FROM T1 JOIN T2 ON T1.ParentTeamID=T2.TeamID
       )
     SELECT TeamTree.* FROM TeamTree JOIN Team T3 
          ON TeamTree.ParentTeamID=T3.TeamID WHERE T3.ParentTeamID IS NULL) teamtree2
     ON Player.TeamID=teamtree2.TeamID;

如果您需要更多列,可以使用Team重新加入。