sql >> Databasteknik >  >> RDS >> Sqlserver

Begränsa rekursion till en viss nivå - Duplicera rader

Detta svar har skrivits om helt. Originalet fungerade inte riktigt under alla omständigheter

Jag var tvungen att ändra CTE för att representera hela enhetshierarkin för varje enhet som en möjlig rot (översta enhet). Det tillåter en sann hierarki med flera barn per enhet.

Jag utökade exempeldatan i denna SQL-fiol att ha en spelare tilldelad både enheterna 11 och 12. Den returnerar korrekt rad för var och en av 3 spelare som spelar för en enhet på någon nivå under enhet 1.

"Root" enhets-ID och listan över spelar-ID finns bekvämt i den yttersta WHERE-satsen längst ner, vilket gör det enkelt att ändra ID:n efter behov.

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)

Här är en något optimerad version som har enhets-ID-kriterierna inbäddade i CTE. CTE beräknar endast hierarkier som är rotade på enheter där föräldra-ID är det valda enhets-ID (1 i detta fall)

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)


Här är mitt ursprungliga svar. Det fungerar bara om enhetshierarkin är begränsad till att endast tillåta ett barn per enhet. SQL Fiddle-exemplet i frågan har 3 barn för enhet 1, så det returnerar felaktigt flera rader för spelare 3, 5 och 6 om det körs mot enhet 1

Här är en SQL-fiol som visar problemet.

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)


  1. PostgreSQL - Alias ​​kolumn och HAVING

  2. bästa handledningen för att använda entitetsramverket, POCO och MySQL

  3. Hur multiplicerar man två kolumnvärden och visar dess resultat i slutet av varje rad?

  4. Säkerhetsfunktioner i SQL Server 2017