Sqlserver
 sql >> база данни >  >> RDS >> Sqlserver

Ограничаване на рекурсията до определено ниво - Дублиране на редове

Този отговор е напълно пренаписан. Оригиналът не работеше съвсем при всички обстоятелства

Трябваше да променя CTE, за да представя пълната йерархия на единиците за всяка единица като възможен корен (горна единица). Позволява истинска йерархия с множество деца на единица.

Разширих примерните данни в този SQL Fiddle да има играч, присвоен и на двете единици 11 и 12. Правилно връща правилния ред за всеки от 3 играчи, които играят за единица на някакво ниво под единица 1.

Идентификаторът на "основния" модул и списъкът с идентификатори на играчи е удобно в най-външната клауза WHERE в долната част, което улеснява промяната на идентификаторите, ако е необходимо.

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. CTE изчислява само йерархии, базирани на единици, където родителският идентификатор е избраният идентификатор на единица (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, така че връща погрешно множество редове за играчи 3, 5 и 6, ако се изпълнява срещу модул 1

Ето 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)


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. DESC и ASC като параметър в съхранена процедура

  2. Генериране на sql код програмно

  3. Актуализирайте всички дублиращи се записи в таблицата в SQL Server освен един

  4. T-SQL премахва всички небуквени и нецифрови знаци

  5. Запитване на данни чрез обединяване на две таблици в две бази данни на различни сървъри