Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

Limiter la récursivité à un certain niveau - Lignes en double

Cette réponse a été complètement réécrite. L'original n'a pas tout à fait fonctionné dans toutes les circonstances

J'ai dû changer le CTE pour représenter la hiérarchie complète des unités pour chaque unité en tant que racine possible (unité supérieure). Il permet une véritable hiérarchie avec plusieurs enfants par Unité.

J'ai étendu les exemples de données dans ce SQL Fiddle pour avoir un joueur affecté aux deux unités 11 et 12. Il renvoie correctement la ligne correcte pour chacun des 3 joueurs qui jouent pour une unité à un certain niveau en dessous de l'unité 1.

L'ID d'unité "racine" et la liste des ID de joueur se trouvent commodément dans la clause WHERE la plus à l'extérieur en bas, ce qui facilite la modification des ID selon les besoins.

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)

Voici une version légèrement optimisée qui intègre les critères d'identification d'unité dans le CTE. Le CTE ne calcule que les hiérarchies enracinées dans les unités où l'ID parent est l'ID d'unité choisi (1 dans ce cas)

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)


Voici ma réponse originale. Cela ne fonctionne que si la hiérarchie des unités est contrainte de n'autoriser qu'un seul enfant par unité. L'exemple SQL Fiddle dans la question a 3 enfants pour l'unité 1, il renvoie donc à tort plusieurs lignes pour les joueurs 3, 5 et 6 s'il est exécuté contre l'unité 1

Voici un SQL Fiddle qui illustre le problème.

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)