18

ネストされたセット モデルとは、ここで説明されていることを意味します。

ユーザー定義の階層に「カテゴリ」を格納するための新しいシステムを構築する必要があります (より適切な言葉は思いつきません)。ネストされたセット モデルは、書き込みではなく読み取り用に最適化されているため、それを使用することにしました。残念ながら、ネストされたセットの調査とテスト中に、ソートされたノードを持つ階層ツリーをどのように表示するかという問題に遭遇しました。たとえば、階層がある場合:

root
    finances
        budgeting
            fy08
    projects
        research
        fabrication
        release
    trash

次のように表示されるように並べ替えます。

root
    finances
        budgeting
            fy08
    projects
        fabrication
        release
        research
    trash

捏造が研究の前に現れることに注意してください。

とにかく、長い検索の結果、「ツリーを多次元配列に格納してソートする」、「ツリーを再ソートし、ネストされたセットモデルにシリアル化する」などの回答が表示されました(言い換えています...)。いずれにせよ、最初の解決策は RAM と CPU の恐ろしい浪費であり、どちらも非常に有限のリソースです。2 番目の解決策は、多くの面倒なコードのように見えます。

とにかく、(ネストされたセットモデルを使用して)方法を理解することができました:

  1. SQL で新しいツリーを開始する
  2. ツリー内の別のノードの子としてノードを挿入します
  3. ツリー内の兄弟ノードの後に​​ノードを挿入します
  4. SQL から階層構造を持つツリー全体をプルする
  5. 深さの制限の有無にかかわらず、階層内の特定のノード (ルートを含む) からサブツリーをプルします
  6. ツリー内の任意のノードの親を見つける

したがって、#5 と #6 を使用して、必要な並べ替えを行うことができ、並べ替えられた順序でツリーを再構築するためにも使用できると考えました。

しかし、私が学んだことをすべて見てきたので、#3、#5、および #6 を一緒に使用して、ソートされた挿入を実行できることがわかりました。ソートされた挿入を行った場合、常にソートされます。ただし、並べ替え基準を変更したり、別の並べ替え順序が必要な場合は、振り出しに戻ります。

これは、ネストされたセット モデルの制限でしょうか? その使用は、出力のクエリソートを阻害しますか?

4

8 に答える 8

5

これは確かに入れ子集合モデルの限界だと思います。ツリー構造を再構築するには結果セットの順序付けが不可欠であるため、子ノードをそれぞれの親ノード内で簡単にソートすることはできません。

ノードを挿入、更新、または削除するときにツリーをソートしておくのがおそらく最善の方法だと思います。これにより、クエリが非常に高速になります。これは、このデータ構造の主な目標の 1 つです。すべての操作にストアド プロシージャを実装すると、非常に使いやすくなります。

並べ替え済みのツリーの並べ替え順序を逆にすることもできます。ORDER BY node.rgt DESCの代わりに使用するだけですORDER BY node.lft ASC

別の並べ替え基準を本当にサポートする必要がある場合は、各ノードに2 番目のインデックスlftとインデックスを追加して実装し、挿入/更新/削除のたびに他の基準で並べ替えることができます。rgt

于 2008-10-14T20:47:23.737 に答える
4

私は入れ子になったセットをたくさん使ってきましたが、同じ問題に頻繁に直面しました。私がしていること、そして私が推奨することは、データベース内のアイテムをソートしないことです。代わりに、ユーザー インターフェイスで並べ替えます。DB からすべてのノードをプルした後、何らかの階層データ構造に変換する必要があります。その構造で、ノードの子を含むすべての配列をソートします。

たとえば、フロントエンドが Flex アプリで、ノードの子が ICollectionView に格納されている場合、sort プロパティを使用して、必要な方法で表示させることができます。

別の例として、フロントエンドが PHP スクリプトからの出力である場合、配列内の各ノードの子を持ち、PHP の配列ソート関数を使用してソートを実行できます。

もちろん、これは実際の db エントリをソートする必要がない場合にのみ機能しますが、そうですか?

于 2009-01-20T16:22:39.213 に答える
2

ネストされたセットツリー全体をソートするのに役立つ次のコードを書き終えました。

並べ替えには (理想的には) ツリー内の各ノードの現在のレベルを一覧表示するビューと、2 つのノードを交換するための手順が必要です。両方とも以下に含まれています。兄弟スワップ コードは、Joe Celkos の「Tree & Hierarchies」の本から入手することを強くお勧めします。ネストされたセットを使用しているすべての人に。

並べ替えは、'INSERT INTO @t' ステートメントで変更できます。ここでは、'Name' の単純な英数字の並べ替えです。

これは、特にセットベースのコードにカーソルを使用する方法としては不十分かもしれませんが、私にとってはうまくいくと言っているので、役に立てば幸いです。

アップデート:

以下のコードは、cusor を使用せずにバージョンを表示するようになりました。約 10 倍の速度向上が見られます

CREATE VIEW dbo.tree_view

AS

SELECT t2.NodeID,t2.lft,t2.rgt ,t2.Name, COUNT(t1.NodeID) AS level  
FROM dbo.tree t1,dbo.tree t2
WHERE t2.lft BETWEEN t1.lft AND t1.rgt
GROUP BY t2.NodeID,t2.lft,t2.rgt,t2.Name

GO

----------------------------------------------

  DECLARE @CurrentNodeID int
DECLARE @CurrentActualOrder int
DECLARE @CurrentRequiredOrder int
DECLARE @DestinationNodeID int
DECLARE @i0 int
DECLARE @i1 int
DECLARE @i2 int
DECLARE @i3 int

DECLARE @t TABLE (TopLft int,NodeID int NOT NULL,lft int NOT NULL,rgt int NOT NULL,Name varchar(50),RequiredOrder int NOT NULL,ActualOrder int NOT NULL)


INSERT INTO @t (toplft,NodeID,lft,rgt,Name,RequiredOrder,ActualOrder)
    SELECT tv2.lft,tv1.NodeID,tv1.lft,tv1.rgt,tv1.Name,ROW_NUMBER() OVER(PARTITION BY tv2.lft ORDER BY tv1.ColumnToSort),ROW_NUMBER() OVER(PARTITION BY tv2.lft ORDER BY tv1.lft ASC)
    FROM dbo.tree_view tv1 
    LEFT OUTER JOIN dbo.tree_view tv2 ON tv1.lft > tv2.lft and tv1.lft < tv2.rgt and tv1.level = tv2.level+1
    WHERE tv2.rgt > tv2.lft+1

    DELETE FROM @t where ActualOrder = RequiredOrder


WHILE EXISTS(SELECT * FROM @t WHERE ActualOrder <> RequiredOrder)
BEGIN


    SELECT Top 1 @CurrentNodeID = NodeID,@CurrentActualOrder = ActualOrder,@CurrentRequiredOrder = RequiredOrder
    FROM @t 
    WHERE ActualOrder <> RequiredOrder
    ORDER BY toplft,requiredorder

    SELECT @DestinationNodeID = NodeID
    FROM @t WHERE ActualOrder = @CurrentRequiredOrder AND TopLft = (SELECT TopLft FROM @t WHERE NodeID = @CurrentNodeID) 

    SELECT @i0 = CASE WHEN c.lft < d.lft THEN c.lft ELSE d.lft END,
            @i1 =  CASE WHEN c.lft < d.lft THEN c.rgt ELSE d.rgt END,
            @i2 =  CASE WHEN c.lft < d.lft THEN d.lft ELSE c.lft END,
            @i3 =  CASE WHEN c.lft < d.lft THEN d.rgt ELSE c.rgt END
    FROM dbo.tree c
    CROSS JOIN dbo.tree d
    WHERE c.NodeID = @CurrentNodeID AND d.NodeID = @DestinationNodeID

    UPDATE dbo.tree
    SET lft = CASE  WHEN lft BETWEEN @i0 AND @i1 THEN @i3 + lft - @i1
                    WHEN lft BETWEEN @i2 AND @i3 THEN @i0 + lft - @i2
            ELSE @i0 + @i3 + lft - @i1 - @i2
            END,
        rgt = CASE  WHEN rgt BETWEEN @i0 AND @i1 THEN @i3 + rgt - @i1
                    WHEN rgt BETWEEN @i2 AND @i3 THEN @i0 + rgt - @i2
            ELSE @i0 + @i3 + rgt - @i1 - @i2
            END
    WHERE lft BETWEEN @i0 AND @i3 
    AND @i0 < @i1
    AND @i1 < @i2
    AND @i2 < @i3

    UPDATE @t SET actualorder = @CurrentRequiredOrder where NodeID = @CurrentNodeID
    UPDATE @t SET actualorder = @CurrentActualOrder where NodeID = @DestinationNodeID

    DELETE FROM @t where ActualOrder = RequiredOrder

END
于 2009-01-19T12:21:33.287 に答える
1

はい、ネストされたセットは階層の事前に順序付けされた表現であるため、ネストされたセットモデルの制限です。この予約注文は、読むのがとても速い理由です. リンク先のページでも説明されている隣接モデルは、最も柔軟な並べ替えとフィルタリングを提供しますが、パフォーマンスに大きな影響を与えます。

ネストされたセットでの挿入と移動に対する私の好ましいアプローチは、影響を受けるブランチを隣接モデルのように処理することです。新しい兄弟のリストを取得します。新しいノードのリスト内の適切な場所を見つけます。必要な更新ステートメントを作成します (これは、本当に注意が必要な部分です)。順序付け基準の変更について: これは 1 回限りのバッチ ジョブであるため、RAM と CPU を吹き飛ばす余裕があります。最も柔軟な答えは、ネストされたセット表現を隣接表現に分割し、ネストされたセットを再構築することです。新しい基準に基づく隣接関係。

于 2008-10-15T11:55:45.353 に答える
0

レンダリングするときに並べ替えることができます。ここでレンダリングについて説明しましたネストされたセットから実際のHTMLツリーにすべてのレコードをレンダリングする方法

于 2010-11-10T10:18:29.223 に答える
0

私のクラスのメソッドから私の簡単な解決策を見てください。$this->table->order は、DB からデータを取得するための Nette フレームワーク コードです。

$tree = Array();
$parents = Array();
$nodes = $this->table->order('depth ASC, parent_id ASC, name ASC');
$i = 0;
$depth = 0;
$parent_id = 0;

foreach($nodes as $node) {
    if($depth < $node->depth || $parent_id < $node->parent_id) {
        $i = $parents["{$node->parent_id}"] + 1;
    }
    $tree[$i] = $node;
    $parents["{$node->id}"] = $i;
    $depth = $node->depth;
    $parent_id = $node->parent_id;
    $i += (($node->rgt - $node->lft - 1) / 2) + 1;
}
ksort($tree);
于 2013-12-08T00:27:11.667 に答える
0

あなたのケースでは、交換したいノードに子孫がない場合、lft と rgt の値を単純に交換できると思います。このツリーを考えてみましょう:

   A
 /   \
B     C
     / \
    D   E

これは、ネストされたセットのこのグループに変わる可能性があります。

1 A 10 
2 B 3  
4 C 9
5 D 6
7 E 8

ここで、D と E を交換するとします。次のネストされたセットは有効で、D と E が交換されます。

1 A 10
2 B 3 
4 C 9 
7 D 8
5 E 6 

もちろん、子の lft 値と rgt 値も更新する必要があるため、サブツリーを持つノードの交換はこの方法では実行できません。

于 2010-02-04T11:28:18.590 に答える
-1

ネストされたセットの並べ替えには制限がなく、難しくはありません。左のバウアー(アンカーなど)で並べ替えるだけで完了です。各ノードにLEVELがある場合は、レベルに基づいて正しいインデントを取得することもできます。

于 2011-01-20T19:42:59.603 に答える