この回答は完全に書き直されました。オリジナルはすべての状況でうまく機能しなかった
CTE を変更して、すべてのユニットの完全なユニット階層を可能なルート (最上位ユニット) として表す必要がありました。これにより、ユニットごとに複数の子を持つ真の階層が可能になります。
このSQL Fiddleのサンプル データを拡張して、ユニット 11 と 12 の両方にプレーヤーが割り当てられるようにしました。ユニット 1 の下のあるレベルでユニットのためにプレーする 3 人のプレーヤーのそれぞれについて、正しい行を適切に返します。
「ルート」ユニット ID とプレーヤー ID のリストは、一番外側の WHERE 句の一番下にあるので、必要に応じて ID を簡単に変更できます。
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 に組み込まれたユニット ID 基準を持つ、わずかに最適化されたバージョンです。CTE は、親 ID が選択されたユニット ID (この場合は 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)
これが私の元の答えです。 ユニット階層が、ユニットごとに 1 つの子のみを許可するように制約されている場合にのみ機能します。質問の SQL Fiddle の例には、ユニット 1 の 3 つの子があるため、ユニット 1 に対して実行すると、プレイヤー 3、5、および 6 の複数の行が誤って返されます。
これは、問題を示す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)