Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Beschränkung der Rekursion auf eine bestimmte Ebene - Doppelte Zeilen

Diese Antwort wurde komplett neu geschrieben. Das Original hat nicht unter allen Umständen funktioniert

Ich musste den CTE ändern, um die vollständige Einheitenhierarchie für jede Einheit als mögliche Wurzel (oberste Einheit) darzustellen. Es ermöglicht eine echte Hierarchie mit mehreren Kindern pro Unit.

Ich habe die Beispieldaten in diesem SQL Fiddle erweitert um einen Spieler beiden Einheiten 11 und 12 zuzuweisen. Es gibt korrekt die richtige Reihe für jeden von 3 Spielern zurück, die für eine Einheit auf einer Ebene unterhalb von Einheit 1 spielen.

Die „Root“-Unit-ID und die Liste der Spieler-IDs befinden sich praktischerweise in der äußersten WHERE-Klausel ganz unten, was es einfach macht, die IDs nach Bedarf zu ändern.

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)

Hier ist eine leicht optimierte Version, bei der die Einheiten-ID-Kriterien in den CTE eingebettet sind. Der CTE berechnet nur Hierarchien, die auf Einheiten basieren, bei denen die Eltern-ID die gewählte Einheiten-ID ist (in diesem Fall 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)


Hier ist meine ursprüngliche Antwort. Es funktioniert nur, wenn die Einheitenhierarchie darauf beschränkt ist, nur ein Kind pro Einheit zuzulassen. Das SQL-Fiddle-Beispiel in der Frage hat 3 untergeordnete Elemente für Einheit 1, sodass es fälschlicherweise mehrere Zeilen für die Spieler 3, 5 und 6 zurückgibt, wenn es gegen Einheit 1 ausgeführt wird

Hier ist ein SQL Fiddle das zeigt das Problem.

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)