ツリーデータをグループ化して集約し、合計する最良の方法は何ですか?
-
05-07-2019 - |
質問
自己参照テーブルを指定
Item
-------------
Id (pk)
ParentId (fk)
関連する値の関連テーブルを使用
ItemValue
-------------
ItemId (fk)
Amount
そしていくつかのサンプルデータ
Item ItemValues
Id ParentId ItemId Amount
-------------------- ----------------------
1 null 1 10
2 1 3 40
3 1 3 20
4 2 4 10
5 2 5 30
6 null
7 6
8 7
Item.Id
を取得し、すべての ItemValue.Amounts
の合計を直接の子に返すために、sprocが必要です。木の下。
たとえば、 1
が渡された場合、ツリーは 2、3、4、5
になり、直接の子は 2、3
出力は次のようになります
ItemId Amount
------------------
2 40 (values from ItemIds 4 & 5)
3 60 (values from ItemId 3)
この動作を実現するには、どのようなアプローチを適用する必要がありますか?
CTEの使用を検討していますが、より良い/より速いアプローチがあるかどうか疑問に思っています。
解決
階層が深くなりすぎないと仮定すると、このような再帰CTEが機能します。
declare @ParentId int;
set @ParentId = 1;
;with
Recurse as (
select
a.Id as DirectChildId
, a.Id
from Item a
where ParentId = @ParentId
union all
select
b.DirectChildId
, a.Id
from Item a
join Recurse b on b.Id = a.ParentId
)
select
a.DirectChildId, sum(b.Amount) as Amount
from Recurse a
left join ItemValues b on a.Id = b.ItemId
group by
DirectChildId;
非CTEメソッドでは、カーソルベースなどの何らかの形の反復が必要になります。それはストアドプロシージャであり、可能性があり、再帰するデータがたくさんある場合、データを適切にスライスする限り、おそらくより良いスケーリングになります。
クラスター化インデックスがIdにある場合、ParentIdに非クラスター化インデックスを追加します。カバリングインデックスとして、ブックマークルックアップなしで初期シークを満たします。クラスター化インデックスは、再帰的な結合に役立ちます。
クラスター化インデックスが既にParentIdにある場合は、Idに非クラスター化インデックスを追加します。一緒に、それらは実質的に上記と同等になります。 ItemValuesの場合、実際のテーブルがこれよりも広い場合、(ItemId)INCLUDE(Amount)のインデックスが必要になる場合があります。
他のヒント
ネストされたセットモデルのようにデータを保存できますか(これはMySQL 参照が、アイデアはデータベース全体で一般的です)?もしそうなら、あなたが探している値を見つけるための操作はかなり簡単でしょう。
これはデータベースで処理する必要がありますか?必要なデータをBLLに取り込み、そこで再帰を実行することをお勧めします。