4

私はmySQLを使用して隣接リストに取り組んでいますが、ノードのセット(最終的な子ノードと一緒に)を移動できる十分なクエリを作成するために必要な思考を(少なくとも自分で)行うことはできません。

テーブルには次の列があります。

 id     name     left     right

どうもありがとう!

4

4 に答える 4

8

これは、単一の入力パラメーター(ノードの新しい左位置(newpos))を使用して、ノードをツリー内の任意の位置に移動できるようにするソリューションです。

基本的に3つのセットがあります:

  • サブツリー用の新しいスペースを作成します。
  • サブツリーをこのスペースに移動します。
  • サブツリーによって空いた古いスペースを削除します。

psuedo-sqlでは、次のようになります。

//
 *  -- create new space for subtree
 *  UPDATE tags SET lpos = lpos + :width WHERE lpos >= :newpos
 *  UPDATE tags SET rpos = rpos + :width WHERE rpos >= :newpos
 * 
 *  -- move subtree into new space
 *  UPDATE tags SET lpos = lpos + :distance, rpos = rpos + :distance
 *           WHERE lpos >= :tmppos AND rpos < :tmppos + :width
 * 
 *  -- remove old space vacated by subtree
 *  UPDATE tags SET lpos = lpos - :width WHERE lpos > :oldrpos
 *  UPDATE tags SET rpos = rpos - :width WHERE rpos > :oldrpos
 */

:distance変数は新しい位置と古い位置の間の距離であり、:widthはサブツリーのサイズであり、:tmpposは更新中に移動されるサブツリーを追跡するために使用されます。これらの変数は次のように定義されます。

// calculate position adjustment variables
int width = node.getRpos() - node.getLpos() + 1;
int distance = newpos - node.getLpos();
int tmppos = node.getLpos();
        
// backwards movement must account for new space
if (distance < 0) {
    distance -= width;
    tmppos += width;
}

完全なコード例については、次のブログを参照してください。

https://rogerkeays.com/how-to-move-a-node-in-nested-sets-with-sql

このソリューションが気に入った場合は、賛成票を投じてください。

于 2012-01-08T05:46:23.167 に答える
4

テーブルは隣接リストではなく、入れ子集合デザインを使用していると確信しています。parent_id隣接リストを使用している場合は、との代わりにのような列がleftありrightます。

移動ノードは、ネストされたセットのロイヤルPITAです。移動する各ノードのすべての値leftと値の番号を付け直す必要があります。right

leftサブツリーを移動する場合、これを行う最も簡単な方法は、ノードを1つずつ削除し、ノードを削除するたびにrightフィールドの番号を付け直すことです。次に、サブツリー全体を削除したら(そして、アプリケーションでサブツリーの構造を何らかの方法で保持したら)、ツリー内の宛先の場所にサブツリーを再挿入し、挿入ごとにフィールドleftとフィールドの番号を付け直します。right


更新:最近、ネストされたセットよりも好きな別の階層データ設計でサブツリーを移動する方法についてブログを書きました。私はこのデザインをクロージャーテーブルと呼んでいます。http://www.mysqlperformanceblog.com/2011/02/14/moving-subtrees-in-closure-table/
を参照してください

于 2010-05-11T02:26:20.087 に答える
4

入れ子集合モデル(左列と右列)を使用してカテゴリツリー内でサブツリーを移動する手順は次のとおりです。1。移動するカテゴリとそのサブカテゴリの負の対応物のlft列とrgt列を変換します(これにより、とりあえず、ツリーからサブツリーを「削除」します)2.サブツリーを上に(または入れ子集合表現では「左」に)移動する場合は、サブツリーの新しい親とその古い左の間ですべてのカテゴリを移動します(または、2番目の場合は右)右に制限します。それ以外の場合(サブツリーを下に移動する場合)は右に制限します。これには、これらのカテゴリの左右の列を、サブツリー(または移動するカテゴリ)の左右の列の間の距離にプラス(または2番目の場合はマイナス)の値に設定することが含まれます。3。この後、

これはすべて非常に複雑に見え、1つのケースで表現されているので、2つのケースに分けました。

$step = 1+ $this->_categoriesTable->rgt
                - $this->_categoriesTable->lft;
$lft = $this->_categoriesTable->lft;
$rgt = $this->_categoriesTable->rgt;
$id = $this->_categoriesTable->id;
$distance = $lft - $parentLeft - 1;
                $query = '
                    UPDATE %s SET lft=-lft, rgt=-rgt
                        WHERE lft>=%d AND lft<=%d;
                    UPDATE %s SET lft=lft+%d WHERE lft>%d AND lft<%d;
                    UPDATE %s SET rgt=rgt+%d WHERE rgt>%d AND rgt<%d;
                    UPDATE %s SET lft=-lft-%d, rgt=-rgt-%d WHERE lft<=-%d
                        AND lft>=-%d;
                    UPDATE %s SET parent_id=%d, title=%s, description=%s,
                        metadescription=%s WHERE id=%s';

                $query = sprintf($query,
                    $this->_db->nameQuote('#__categories'),
                        $lft, $rgt,
                    $this->_db->nameQuote('#__categories'), $step,
                        $parentLeft, $lft,
                    $this->_db->nameQuote('#__categories'), $step,
                        $parentLeft, $lft,
                    $this->_db->nameQuote('#__categories'), $distance,
                        $distance, $lft, $rgt,
                    $this->_db->nameQuote('#__categories'),
                        $data['parent_id'], 
                        $this->_db->Quote($this->_categoriesTable->title),
                        $this->_db->Quote($this->_categoriesTable->description),
                        $this->_db->Quote(
                            $this->_categoriesTable->metadescription),
                        $this->_db->Quote($id));

// and for the moving to the "right" case
$step = 1+ $this->_categoriesTable->rgt
                - $this->_categoriesTable->lft;
$distance = $parentLeft - $this->_categoriesTable->rgt;

// Memorize this because we bind and we need the old values
$lft = $this->_categoriesTable->lft;
$rgt = $this->_categoriesTable->rgt;
$id = $this->_categoriesTable->id;

$query = sprintf($query,
                    $this->_db->nameQuote('#__categories'),
                        $lft, $rgt,
                    $this->_db->nameQuote('#__categories'), $step,
                        $rgt, $parentLeft,
                    $this->_db->nameQuote('#__categories'), $step,
                        $rgt, $parentLeft,
                    $this->_db->nameQuote('#__categories'), $distance,
                        $distance, $lft, $rgt,
                    $this->_db->nameQuote('#__categories'),
                        $data['parent_id'],
                        $this->_db->Quote($this->_categoriesTable->title),
                        $this->_db->Quote($this->_categoriesTable->description),
                        $this->_db->Quote(
                            $this->_categoriesTable->metadescription),
                        $this->_db->Quote($id));
于 2010-12-29T19:14:35.237 に答える
3

私はよりシンプルで読みやすいSQLを手に入れました、それは私にとって完璧に機能します。これは、id、rgt、lft、level(レベルなしでも機能します)を持つ典型的なネストされたセット構造を作成しました。

#Set IDs
SET @dirId := :dirId; #folder (subtree) you wanna move
SET @targetId := :folderId; #target

#get datas
SELECT rgt, lft, rgt-lft+1, level INTO @dir_rgt, @dir_lft, @dir_size, @dir_level FROM files WHERE id = @dirId;

#put the moving tree aside (lft and rgt columns must allow negative int)
UPDATE files SET lft = 0-lft, rgt = 0-rgt WHERE lft BETWEEN @dir_lft AND @dir_rgt;

#fill the empty space        
UPDATE files SET rgt = rgt-@dir_size WHERE rgt > @dir_rgt;
UPDATE files SET lft = lft-@dir_size WHERE lft > @dir_rgt;

#get datas of the target-folder      
SELECT lft, level INTO @target_lft, @target_level FROM files WHERE id = @targetId;

#create space in the target-folder        
UPDATE files SET rgt = rgt+@dir_size WHERE rgt >= @target_lft;
UPDATE files SET lft = lft+@dir_size WHERE lft > @target_lft;

#edit all nodes in the moving-tree
UPDATE files SET
   lft     = 0 - lft - (@dir_lft - @target_lft - 1), #this formula fits for all moving directions
   rgt     = 0 - rgt - (@dir_lft - @target_lft - 1), 
   level   = level - (@dir_level - @target_level) + 1

WHERE 
   lft < 0; #that could be more precise...
于 2015-02-27T16:53:13.093 に答える