sql >> データベース >  >> RDS >> Sqlserver

再帰を特定のレベルに制限する - 重複行

    この回答は完全に書き直されました。オリジナルはすべての状況でうまく機能しなかった

    CTE を変更して、すべてのユニットの完全なユニット階層を可能なルート (最上位ユニット) として表す必要がありました。これにより、ユニットごとに複数の子を持つ真の階層が可能になります。

    この SQL Fiddle のサンプル データを拡張しました ユニット 11 と 12 の両方にプレーヤーを割り当てます。ユニット 1 の下のあるレベルのユニットでプレーする 3 人のプレーヤーのそれぞれについて、正しい行を適切に返します。

    「ルート」ユニット ID とプレーヤー ID のリストは、必要に応じて ID を簡単に変更できるように、下部の最も外側の 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)
      

    これは、ユニット ID 基準が CTE に埋め込まれた、わずかに最適化されたバージョンです。 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)
      

    1. Doctrine2.3とOneToOneカスケードが持続しないようです

    2. 再生の紹介—SpotlightCloudの新しいポイントインタイム診断

    3. SQLServerで列名またはテーブル名の名前を変更する方法-SQLServer/T-SQLチュートリアルパート36

    4. データベースのデフォルト値のDjango実装