2011-10-22 25 views
2

私は単純な問題であるように思えますが、これまでに試したソリューションでは、実行領域で私に欲しいものが残っています。小さい(<10000)のデータセットではスピードが上がっているようですが、カウントが上がるにつれてすばやく時間がかかります。FOREACH再帰的SQLステートメント

SQL Server 2008 R2では、4つの列(Id、ParentId、ControlNum、ParentControlNum)を持つテーブルがあります。

IdとParentId情報が入力されます。Idには常に値があり、行に親がない場合はParentIdがNULLになり、そうでない場合は親行を表すテーブル内のIdの値になります。

問題は、IdとParentIdsがすべての場所にあることです。すべてのIDがテーブルに追加され、子プロセスが追加されます。これは問題の一部であり、変更可能なものではありません。

私ができる必要があるのは、親子関係に従うためにControlNum値を生成することです。私の現在のロジックは、これを達成するためにC#とSQLのSELECT/UPDATEコマンドを少し使用しますが、前述のようにパフォーマンスは大きな問題です。擬似コードで

Select all Id's where the parent Id is null (All root entries) 
Foreach (Id) 
    GenerateControlNum(Id, CurrentCounterValue, CurrentCounterValue) 

GenerateControlNum(Id, CurrentCounterValue, ParentCounterValue) 
    Set Id's ControlNum to CurrentCounterValue 
    Set Id's ParentControlNum to CurrentCounterValue 

    Increment CurrentCounterValue 

    Select All Id's where ParentId == Id (All my direct children) 
    Foreach (ChildId) 
     GenerateControlNum(ChildId, CurrentCounterValue, Id's ControlNum); 

ベースラインを兼ね備えことになるSQLでidealy完全に、これはより速く実行させることを試みています。私はCTEの行に沿ってRootIdsを設定し、それからMERGEステートメントを実行していますが、ControlNum値を設定するためにカウンタ値が正しく動作するようには見えません。

これはSQLでも可能ですか、これは手続き型の処理が多すぎますか?それが現在実行方法から

例表データ:

ID          ParentId        ControlNum ParentControlNum 
8C821027-A6F9-E011-AB48-B499BAE13A62 756F981E-A6F9-E011-AB48-B499BAE13A62 0   NULL 
D7DB6033-A6F9-E011-AB48-B499BAE13A62 756F981E-A6F9-E011-AB48-B499BAE13A62 0   NULL 
D2E36033-A6F9-E011-AB48-B499BAE13A62 C9E36033-A6F9-E011-AB48-B499BAE13A62 0   NULL 
8FE66033-A6F9-E011-AB48-B499BAE13A62 58E66033-A6F9-E011-AB48-B499BAE13A62 0   NULL 
37EC6033-A6F9-E011-AB48-B499BAE13A62 2FEC6033-A6F9-E011-AB48-B499BAE13A62 0   NULL 
41EC6033-A6F9-E011-AB48-B499BAE13A62 2FEC6033-A6F9-E011-AB48-B499BAE13A62 0   NULL 
DDED6033-A6F9-E011-AB48-B499BAE13A62 BCED6033-A6F9-E011-AB48-B499BAE13A62 0   NULL 
DC69981E-A6F9-E011-AB48-B499BAE13A62 NULL         0   NULL 
166A981E-A6F9-E011-AB48-B499BAE13A62 NULL         0   NULL 
4D6A981E-A6F9-E011-AB48-B499BAE13A62 NULL         0   NULL 
856A981E-A6F9-E011-AB48-B499BAE13A62 NULL         0   NULL 
F56A981E-A6F9-E011-AB48-B499BAE13A62 NULL         0   NULL 
2E6B981E-A6F9-E011-AB48-B499BAE13A62 NULL         0   NULL 
666B981E-A6F9-E011-AB48-B499BAE13A62 NULL         0   NULL 
9D6B981E-A6F9-E011-AB48-B499BAE13A62 NULL         0   NULL 

ID          ParentId        ControlNum ParentControlNum 
8C821027-A6F9-E011-AB48-B499BAE13A62 756F981E-A6F9-E011-AB48-B499BAE13A62 22   21 
D7DB6033-A6F9-E011-AB48-B499BAE13A62 756F981E-A6F9-E011-AB48-B499BAE13A62 24   21 
D2E36033-A6F9-E011-AB48-B499BAE13A62 C9E36033-A6F9-E011-AB48-B499BAE13A62 58   57 
8FE66033-A6F9-E011-AB48-B499BAE13A62 58E66033-A6F9-E011-AB48-B499BAE13A62 69   68 
37EC6033-A6F9-E011-AB48-B499BAE13A62 2FEC6033-A6F9-E011-AB48-B499BAE13A62 86   85 
41EC6033-A6F9-E011-AB48-B499BAE13A62 2FEC6033-A6F9-E011-AB48-B499BAE13A62 88   85 
DDED6033-A6F9-E011-AB48-B499BAE13A62 BCED6033-A6F9-E011-AB48-B499BAE13A62 95   94 
DC69981E-A6F9-E011-AB48-B499BAE13A62 NULL         0   0 
166A981E-A6F9-E011-AB48-B499BAE13A62 NULL         1   1 
4D6A981E-A6F9-E011-AB48-B499BAE13A62 NULL         2   2 
856A981E-A6F9-E011-AB48-B499BAE13A62 NULL         3   3 
F56A981E-A6F9-E011-AB48-B499BAE13A62 NULL         4   4 
2E6B981E-A6F9-E011-AB48-B499BAE13A62 NULL         5   5 
666B981E-A6F9-E011-AB48-B499BAE13A62 NULL         6   6 
9D6B981E-A6F9-E011-AB48-B499BAE13A62 NULL         7   7 

後、私が持っているデータセットは、今104件のエントリーをされる前に、ので、これはとのちょうど最初の15のオブジェクトであります親が下にある場合、それらはルートエントリの例であり、コントロール番号と親コントロール番号は同じ値に設定されています。テーブルの最上部には、親が同じで、親コントロールの番号が一致し、コントロール番号がかなり近いものがいくつか表示されています(親21のControlNum 22と24の間に行がなければなりません)。 88ジャンプ、彼らはちょうどお互いの隣にテーブルにはありません)。

これがより明確になることを願っています。

EDIT:ミカエル

以下

によって与えられた回答に基づいてより多くの明快さは、彼らのイドとParentIdは情報に基づいて階層構造で表示ControlNum値です。通常、これらは1,2,3、... 8と表示されますが、メッセージ全体(メッセージの子)でディスプレイを乱雑にしないほうが簡単です。私は

1 
    2 
    3 
    4 
    5 
6 
    7 
8 

再帰がされている理由は、私が行くされている方法は、私はルートオブジェクトにControlNumを割り当てる必要があるされ、その後、次のオブジェクトをする必要がある必要なもの

1 
    4 
    7 
    8 
    5 
2 
    6 
3 

次のルートオブジェクトに進む前に、その最初の子であるか、そのオブジェクトの子であるかなどがあります。

私はこれが幅広い最初であり、私が必要としているのは深みが最初であると言います。

+0

サンプルデータを追加できますか?更新前の外観と更新後の外観「親子関係に従うためにControlNum値を生成する」という意味がわかりません –

+0

@MikaelEriksson説明が少し追加されたサンプルデータです。 – James

答えて

3

あなたの要件をすべて取得するかどうかわかりませんが、こちらが開始です。あなたが望むことをしているか、あるいは数字が正しく生成されていないか教えてください。

;with C as 
(
    select ID, 
     ParentID, 
     ControlNum, 
     ParentControlNum, 
     row_number() over(order by ParentID, ID) - 1 as rn 
    from YourTable 
) 
update C1 
set ControlNum = C1.rn, 
    ParentControlNum = case when C1.ParentID is null 
         then C1.rn 
         else C2.rn 
         end 
from C as C1 
    left outer join C as C2 
    on C1.ParentID = C2.ID 

わずかに修正された入力を有するSE-データに対して実行し、それを:http://data.stackexchange.com/stackoverflow/q/115625/

版2

値を生成する際に順として使用する文字列を構築まず再帰CTE RControlNum。その後は、上記とほとんど同じです。

;with R as 
(
    select ID, 
     ParentID, 
     cast(ID as varchar(max)) as Sort 
    from YourTable 
    where ParentID is null 
    union all 
    select T.ID, 
     T.ParentID, 
     R.Sort+cast(T.ID as varchar(max)) 
    from YourTable as T 
    inner join R 
     on R.ID = T.ParentID 
), 
C as 
(
    select ID, 
     ParentID, 
     row_number() over(order by Sort) - 1 as rn 
    from R 
) 
update T 
set ControlNum = C1.rn, 
    ParentControlNum = case when C1.ParentID is null 
         then C1.rn 
         else C2.rn 
         end 
from YourTable as T 
    inner join C as C1 
    on T.ID = C1.ID 
    left outer join C as C2 
    on T.ParentID = C2.ID 

ここでテスト:http://data.stackexchange.com/stackoverflow/q/115626/

注:新しいノードを追加する苦労を持っていると同時に、同様の番号を維持しますので、私は、これはあなたには、いくつかのデータで行います一度の事だと思いますこの。たとえば、最初のノードに新しい子ノードを追加する場合は、すべてのノードに対してすべてControlNum += 1を「下位」に割り当てて、すべてParentControlNumを再割り当てする必要があります。

+0

これは正しい方向に大きなステップです。しかし、私の問題は完全には解決されていません。 – James

+0

これは実際に処理する前にデータを最終的に順序付けするためのものです。おそらく文字列比較のためにこれは動作しますが、少し遅くなります。私は、最初のバージョンから与えられたデータをParentControlNumによって順序付けし、結果にrow_number()を使用する可能性を熟考しています。Anywho、将来の進化について考えてみてください。もう一度実用的な答えをありがとう。 – James