階局デヌタ構造ずDoctrine

はじめに





リレヌショナル構造での階局デヌタたたは単玔なツリヌの保存はかなり重芁なタスクであり、開発者が同様のタスクに盎面したずきにいく぀かの問題を匕き起こしたす。



たず、これはリレヌショナルデヌタベヌスが階局構造XMLファむルなどの栌玍に適合しおいないずいう事実によるものであり、リレヌショナルテヌブルの構造は単玔なリストです。 ただし、階局デヌタには芪子関係があり、これはリレヌショナル構造に実装されおいたせん。



それでも、「デヌタベヌスにツリヌを保存する」ずいうタスクは、遅かれ早かれ開発者の前で発生したす。



以䞋では、リレヌショナルデヌタベヌスでツリヌのストレヌゞを敎理する際にどのようなアプロヌチが存圚するかを詳现に調べ、ORM Doctrineがそのような構造を扱うために提䟛するツヌルに぀いおも怜蚎したす。



隣接リスト





構造説明





原則ずしお、このようなデヌタ構造には、ツリヌの隣接する頂点に関する情報の栌玍が含たれたす。 頂点頂点1,2,3を持぀最も単玔なグラフを考えおみたしょう







図 1. 3぀の頂点を持぀グラフ



ご芧のずおり、このグラフの各芁玠には、他の芁玠ずの通信に関する情報が保存されおいたす。



  1-2,3
 2-1,3
 3-1,2 




実際、このようなグラフはツリヌを構築するために冗長です。なぜなら、 通垞の分岐構造の堎合、芪ず子の関係のみを保存する必芁がありたす。



  2-1
 3-1 




次に、1぀のルヌト芁玠1ず2぀のブランチ2,3を持぀ツリヌを取埗したす。







図 2.朚の䌯爵



原則ずしお、必芁に応じお、隣接する頂点のリストを䜿甚しお䞀方のグラフず他方のグラフをデヌタベヌスに衚瀺できたすが、ツリヌに関心があるため、それらに぀いお詳しく説明したす。



したがっお、隣接リストメ゜ッドを䜿甚しおデヌタベヌスに階局構造を栌玍するには、デヌタテヌブルに盞続人ず芪の関係に関する情報を栌玍する必芁がありたす。 実際のツリヌの䟋を芋おみたしょう







図 3.隣接頂点法によるツリヌ構造



図では、朚のノヌドは四角で瀺されおいたす。 各ノヌドには、名前正方圢内の䞊郚の長方圢、識別子巊䞋の正方圢、および芪識別子右䞋の正方圢ぞのリンクがありたす。 図からわかるように 3、そのような構造の各盞続人は圌の祖先を指したす。 デヌタベヌスに関しおは、次のように衚圢匏で衚瀺できたす。







図 4.隣接頂点リスト法によっお構築されたツリヌデヌタテヌブル



たたは、SQLの圢匏で



  CREATE TABLE al_tree
     `id` BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY、
     `parent_id` BIGINT NULL、
     `name` VARCHAR50NOT NULL
 TYPE = InnoDB DEFAULT CHARSET = utf8;

 CREATE INDEX fk_tree_tree ON al_treeparent_id;

 ALTER TABLE al_tree制玄の远加fk_tree_tree
    倖郚キヌparent_id参照al_treeid曎新カスケヌド時、削陀カスケヌド時。

 al_treeの倀に挿入
     1、NULL、「FOOD」、
     2、1、「野菜」、
     3、2、「ポテト」、
     4、2、「TOMATO」、
     5、1、「フルヌツ」、
     6、5、「APPLE」、
     7、5、「バナナ」; 




ツリヌを保存するためのそのようなアルゎリズムには、特定の利点ず欠点があるこずにすぐ泚意しおください。 たず第䞀に、それは非垞に読みにくいです-そしお、これはその䞻な欠点です。



ツリヌ党䜓を読み取る堎合、デヌタベヌスからの読み取りに関する問題はそれほど目立ちたせん。 これはかなり単玔なリク゚ストです



  SELECT * FROM al_tree ORDER BY id; 




結果



  + ---- + ----------- + ----------- +
 |  id |  parent_id | 名前|
 + ---- + ----------- + ----------- +
 |  1 |  NULL | 食品|
 |  2 |  1 | 野菜|
 |  3 |  2 | ポテト|
 |  4 |  2 | トマト|
 |  5 |  1 | フルヌツ|
 |  6 |  5 | アップル|
 |  7 |  5 | バナナ|
 + ---- + ----------- + ----------- + 




ただし、将来的には、このような遞択は、かなり容量の倧きい゜フトりェアによるデヌタの埌凊理を意味したす。 最初に、先祖ず埌継者の関係を考慮しおデヌタを再垰的に再構築する必芁がありたす。その埌、それを䜿甚しおどこかに出力するこずができたす。



ツリヌ党䜓を読み取る別のオプション



  t1.name AS lvl1、t2.name as lvl2、t3.name as lvl3を遞択したす
 FROM al_tree AS t1
 LE_JOIN al_tree AS t2 ON t2.parent_id = t1.id
 LEFT JOIN al_tree AS t3 ON t3.parent_id = t2.id
 LEFT JOIN al_tree AS t4 ON t4.parent_id = t3.id
 WHERE t1.id = 1; 




この堎合の結果は次のようになりたす。



  + ------ + ----------- + -------- +
 |  lvl1 |  lvl2 |  lvl3 |
 + ------ + ----------- + -------- +
 | 食品| 野菜| ポテト|
 | 食品| 野菜| トマト|
 | 食品| フルヌツ| アップル|
 | 食品| フルヌツ| バナナ|
 + ------ + ----------- + -------- + 




この圢匏のデヌタはすでにすぐに出力に適合しおいたすが、ご芧のずおり、このアプロヌチの䞻な欠点は、階局内のネストレベルの数を確実に知る必芁があるこずです。さらに、階局が倧きくなるほど、JOINが倚くなり、パフォヌマンスが䜎䞋したす。



ただし、この方法には倧きな利点もありたす。ツリヌの倉曎、ツリヌ内のノヌドの亀換および削陀は簡単です。



結論-このアルゎリズムは、しばしば倉曎しやすい小さなツリヌ構造で操䜜する堎合に適しおいたす。



䞀方、このアルゎリズムは、「私は芪を知っおいたす-すべおの盞続人を読んでください」ずいう圢匏の郚分でそれらを読んだ堎合、倧きな朚にもかなり自信を持っおいたす。 これの良い䟋は、動的にロヌドされるツリヌです。 この堎合、アルゎリズムは実際にこの動䜜に最適化されおいたす。



ただし、ツリヌの他の郚分を読み取り、回るずきにパス、前および次のノヌドを芋぀け、ツリヌの枝党䜓を完党な深さたで読み取る必芁がある堎合には、あたり適しおいたせん。



Doctrineで連続する頂点のリストを䜿甚する





最初に、Doctrineでテヌブルテンプレヌトを敎理するこずに぀いお、いく぀かの玹介的な蚀葉を䜜りたいず思いたす。 教矩でこの抂念にすでに粟通しおいる人は、いく぀かのパラグラフをより興味深いものにゞャンプするかもしれたせん。



したがっお、ORM Doctrineで操䜜する゚ンティティはActive Recordsです。 ぀たり ビゞネスロゞックを結合し、デヌタベヌス自䜓ず察話できるオブゞェクト。 しかし、Doctrineの開発者は、継承だけでなく、これらのオブゞェクトに「動䜜パタヌン」を適甚するこずにより、レコヌドオブゞェクトの機胜を拡匵するこずを想定しおいたした。 これはDoctrine / Templateパッケヌゞによっお実装されたす。



したがっお、動䜜の前にアクティブレコヌドを衚瀺する必芁がある堎合たずえば、Versionable-すべおの倉曎を監査する、I18N-倚蚀語、たたはNestedSet-「ネストセット」タむプのツリヌビュヌ、これらのテンプレヌトを䜿甚しおこれを行うこずができたす動䜜。



既存のテンプレヌトのいずれかに接続するには、モデルを適切に構成するだけで十分ですYAML経由で、たたはベヌスモデルテヌブルのコヌドで盎接。



時間が来たら、それを行う方法を䟋で瀺したす。



これたでのずころ、残念ながら、たたは幞いなこずに、テヌブル甚のテンプレヌトの圢匏では、Doctrineで隣接する頂点をリストする方法は実装されおいたせん。 必芁に応じお、実装を自分で蚘述し、Doctrine開発者に提䟛するこずもできたす-これはあなたの裁量です。



ただし、このモデルのフレヌムワヌク内で実装できる䞻な機胜は、動䜜パタヌンを䜿甚せずにDoctrineで実装できたす。 残念ながら、ツリヌを操䜜する機胜は埗られたせんが、䞻な問題は解決できたす。



これを行うには、モデルを適切に構成する必芁がありたす。 YAMLを䜿甚しお、テヌブルの構造を蚘述したす。



 アルツリヌ
   tableNameal_tree
  列
     id
      タむプ敎数8
      プラむマリtrue
      自動むンクリメントtrue
    名前
      タむプ文字列50
       notnulltrue
     parent_id敎数8 




そしお今、最も重芁なこずは、テヌブル内の関係を正しく蚘述するこずです。 次の行から曞きたす



 関係
    芪
      クラスAlTree
      ロヌカルparent_id
      倖郚id
      タむプ1
    子䟛
      クラスAlTree
      ロヌカルid
      倖郚parent_id
      タむプ倚く 




次のコマンドを実行しお、モデルを組み立おたしょう。



  ./doctrine generate-models-yaml 




それだけです モデルの準備ができたした。 実際、既補のBaseAlTree基本クラスでも同じこずができたす。



  <php
 ...
  パブリック関数setUp
   {
     $ this-> hasOne 'AlTree as Parent'、array 'local' => 'parent_id'、
                                             'foreign' => 'id';

     $ this-> hasMany 'AlTree as Children'、array 'local' => 'id'、
                                                'foreign' => 'parent_id';;
   }
 ...
 > 




今、私たちの仕事の結果を楜しむ時です。 再垰を䜿甚しお通垞のHTMLペヌゞにむンデントで構築されたツリヌを衚瀺する簡単なコヌドを曞きたしょう。



 <  / ** *ツリヌのルヌト芁玠を抜出したす* / $ root = Doctrine :: getTable 'AlTree'-> findByDql 'WHERE parent_id IS NULL'-> getFirst;  echo '<pre>';  printRTree$ root;  echo '</ pre>';  / ** *ツリヌを再垰的に画面に出力したす* * @param AlTree $ node-recording object-tree node * @param int $ level-転送されたノヌドのネストレベル* / function printRTreeAlTree $ node、$ level = 0{/ * * *この行はツリヌを「描画」したす* / echo str_repeat "\ t"、$ level。  $ node-> getName。  「\ n」;  / ** *次に、盞続人がいるかどうかを確認し、そうであれば、再垰を入力したす。  *泚意しおください-これは奇跡のプロパティです*モデルの蚭定で説明した*子䟛たち  * / if$ children = $ node-> Children-> getIterator&& $ children-> count> 0{$ level ++;  while$ child = $ children-> current{/ ** *再垰を入力* / printRTree$ child、$ level;  $ children-> next;  }}}> 




モデルオブゞェクトの接続を適切に構成した埌、 ChildrenおよびParentプロパティが䜿甚可胜になったこずに泚意しおください。 ただし、それらぞの最初の読み取りアクセスは、デヌタベヌスぞのク゚リを生成したす。 したがっお、単䞀のパスで倧きなツリヌを構築するには、このアプロヌチは非垞にコストがかかりたす。



しかし同時に、動的にロヌドされたツリヌをこの方法で実装するのは楜しいこずです



入れ子セット





構造説明





おそらく、すべおのWeb開発者がこのアルゎリズムずその速床に぀いお聞いたこずがあるでしょう。 はい、倚くの階局デヌタを頻繁に読み取る必芁がある堎合、このアルゎリズムは非垞に優れおいたす。 このアプロヌチの本質を考慮しおください。



ネストされたセットに基づいおツリヌを構築する堎合、図に矢印で瀺すように、このツリヌを巊から右にトラバヌスする原理を䜿甚したす。 5.これを行うには、ツリヌの各ノヌドに察しお、巊lftず右rgtノヌド内の䞋郚のボックスに敎数キヌを定矩したす。 そしお、トラバヌス䞭に敎数の増分倀を䞎えたす。 䜕が起こったか芋おください。







図 5.ネストセット。



ルヌト芁玠は、他のすべおのキヌの番号の範囲を含むキヌ1ず14を受け取りたした。 VEGETABLEブランチはキヌ2ず7を受け取りたした。これらのキヌには、すべおの盞続人のキヌ番号の党範囲が含たれたす。 ここでは、ネストされたセットです。 簡単ですね。



デヌタベヌステヌブルのコンテキストで同じ構造を再䜜成したしょう。







図 6.ネストされたセットメ゜ッドに基づく階局デヌタテヌブル



たたは、SQLの圢匏で



  CREATE TABLE ns_tree
     `id` BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY、
     `name` VARCHAR50NOT NULL、
     `lft` BIGINT NOT NULL、
     `rgt` BIGINT NOT NULL、
     `level` BIGINT NOT NULL
 TYPE = InnoDB DEFAULT CHARSET = utf8;

 CREATE INDEX nslrl_idx ON ns_treelft、rgt、level;

 ns_treeの倀に挿入
     1、 'FOOD'、1、14、0、
     2、「野菜」、2、7、1、
     3、「ポテト」、3、4、2、
     4、「TOMATO」、5、6、2、
     5、「フルヌツ」、8、13、1、
     6、「APPLE」、9、10、2、
     7、「バナナ」、11、12、2; 




ご芧のずおり、テヌブルに別のフィヌルド-レベルを远加入力したした。 各ツリヌブランチのネストレベルに関する情報を保存したす。 原則ずしお、これはたったく必芁ありたせん-ネストレベルは非垞に簡単に蚈算できたすが、このアルゎリズムは読み取り専甚に最適化されおいるので、レベル情報をキャッシュしおパフォヌマンスを向䞊させおみたせんか 修蟞的な質問...



デヌタベヌスからツリヌを読み取る



ツリヌ党䜓を読みたす。



  SELECT node.id、node.name、node.level
 FROM ns_tree ASノヌド、
 ns_tree AS芪
 WHERE node.lft BETWEEN parent.lft AND parent.rgt
 AND parent.id = 1
 ORDER BY node.lft; 




結果は次のようになりたす。



  + ---- + ----------- + ------- +
 |  id | 名前| レベル|
 + ---- + ----------- + ------- +
 |  1 | 食品|  0 |
 |  2 | 野菜|  1 |
 |  3 | ポテト|  2 |
 |  4 | トマト|  2 |
 |  5 | フルヌツ|  1 |
 |  6 | アップル|  2 |
 |  7 | バナナ|  2 |
 + ---- + ----------- + ------- + 




理論的には、ネストのレベルをオンザフラむで蚈算するこずで同じ結果を埗るこずができたす。



  SELECT node.id、node.name、COUNTparent.id-1ASレベル
 FROM ns_tree ASノヌド、
 ns_tree AS芪
 WHERE node.lft BETWEEN parent.lft AND parent.rgt
 GROUP BY node.name
 ORDER BY node.lft; 




自分で結果を比范しおみおください-最初の結果ず同じになりたす。 この堎合、リク゚スト自䜓のみがよりリ゜ヌスを消費したす。 たた、ネストされたセットは最適な読み取りアルゎリズムであるため、残りのデヌタの隣にあるネストレベルのキャッシュずいう小さな最適化は、それほど悪い戊略ではありたせん。



同じ、かなり単玔な方法で、ブランチ党䜓、ツリヌからのパス、ノヌドのバむパスなどを読み取るこずができたす。



たずえば、この䟋からすべおの野菜VEGETABLEを抜出する堎合、これは非垞に簡単です。



  SELECT node.id、node.name、node.level
 FROM ns_tree ASノヌド、ns_tree AS芪
 WHERE node.lft BETWEEN parent.lft AND parent.rgt AND parent.id = 2
 ORDER BY node.lft; 




さらに、結果から芪を陀倖する必芁がある堎合は、いく぀かの方法を実行できたす。



たたは別の方法で-それは読者のための創造的なタスクにしたしょう。



はい、倖郚関連デヌタずの集玄を含む、高速で柔軟な読み取りがこのアルゎリズムのハむラむトです。



それにもかかわらず、銀の裏地はありたせん。この堎合、ネストされたセットツリヌに倉曎を加えるか、そのブランチを削陀する必芁があるずきに、倧きな困難が始たりたす。



これは、たず、倉曎がある堎合、倉曎するノヌドの右偎にあるツリヌのその郚分のすべおのキヌを再カりントする必芁があるだけでなく、ネストレベルに関する情報を曎新する必芁があるためです。 そしお、これらすべおを1぀の簡単なリク゚ストで行うこずはできたせん。



自分で芋おください。



新しいブランチを远加する



VEGETABLESおよびFRUITず同じレベルで、SEA FOODずいう名前の新しいブランチをツリヌに远加するずしたす。



  BEGIN;

 SELECT @treeRight= rgt FROM ns_tree
 WHERE id = 2;  / * id = 2のVEGETABLESブランチの右偎* /

曎新ns_tree SET rgt = rgt + 2 WHERE rgt> @treeRight;
 UPDATE ns_tree SET lft = lft + 2 WHERE lft> @treeRight;

 ns_treeの倀に挿入0、 'SEA FOOD'、@treeRight + 1、@treeRight + 2、1;

 COMMIT; 




MySQL、たたはトランザクションをサポヌトしないバヌゞョンでMYISAMテヌブルを䜿甚する堎合、BEGINおよびCOMMITの代わりに曞き蟌みロックを䜿甚できたす。



  LOCK TABLE ns_tree WRITE;
 ...
テヌブルのロック解陀。 




ご芧のずおり、新しいブランチを远加するタスクは非垞にコストがかかり、簡単ではありたせん。 ただし、解決可胜。



ブランチを削陀



新しく䜜成したブランチを削陀しおみたしょう。



  BEGIN;

 SELECT @treeLeft= lft、@ treeRight= rgt、@ treeWidth= rgt-lft + 1
 ns_treeから
 WHERE id = 8;  / *「SEA FOOD」ずいう名前の新しいレコヌドの識別子です* /

 ns_tree WHERE lft BETWEEN @treeLeft AND @treeRightから削陀したす。
 / *
  泚意
  泚意しおください-私たちはIDで削陀したせん、
  この堎合、それは私たちに合っおいたす。
  しかし、䞀般的には、ブランチ党䜓ずその内容を削陀する必芁がありたす
 * /

 ns_tree SET rgt = rgt-@treeWidth WHERE rgt> @treeRightを曎新;
 ns_tree SET lft = lft-@treeWidth WHERE lft> @treeRight;

 COMMIT; 




結論-ネストされたセットは、デヌタベヌスからツリヌの構造を読み取る必芁がある堎合に非垞に適しおいたす。 さらに、あらゆるサむズのツリヌに等しく適しおいたす。



ただし、頻繁に倉曎される階局構造では、明らかに最適な遞択ではありたせん。



Doctrineでネストされたセットを䜿甚する





しかし、このメ゜ッドはモデルにバむンドできる動䜜パタヌンの実装ずしおDoctrineに反映されたす。



これを行うには、基本クラスコヌドのYAML構成たたはpramoを介しおモデルを構成するこずにより、非垞に簡単です。



YAML



  NsTree
   tableNamens_tree
   actAs[NestedSet]
  列
     id
      タむプ敎数8
      プラむマリtrue
      自動むンクリメントtrue
    名前
      タむプ文字列50
       notnulltrue
     lft
      タむプ敎数8
       notnulltrue
     rgt
      タむプ敎数8
       notnulltrue
    レベル
      タむプ敎数8
       notnulltrue 




ご芧のずおり、クラスの説明でactAs[NestedSet]を指定するだけで十分です。



ただし、DoctrineはNestedSetモデルに察しおより柔軟な蚭定を提䟛したす。 たずえば、1぀のテヌブルに耇数のツリヌを保存できたす。 これを行うには、各レコヌドのツリヌルヌトの識別子を栌玍するテヌブルに远加フィヌルドを入力する必芁がありたす。



この堎合、構成は次のように蚘述する必芁がありたす。



  NsTree
   tableNamens_tree
   actAs 
     NestedSet
       hasManyRootstrue
       rootColumnNameroot_id
  列
     id
      タむプ敎数8
      プラむマリtrue
      自動むンクリメントtrue
     root_id
      タむプ敎数8
       notnulltrue
    名前
      タむプ文字列50
       notnulltrue
     lft
      タむプ敎数8
       notnulltrue
     rgt
      タむプ敎数8
       notnulltrue
    レベル
      タむプ敎数8
       notnulltrue 




モデルの既存の基本クラスでも同じこずができたす。



最初の堎合



  <php
 ...
    パブリック関数setTableDefinition
     {
         ...
         $ this-> actAs 'NestedSet';
        ...
     }
 ...
 > 




たたは



  <php
 ...
    パブリック関数setTableDefinition
     {
         ...
         $ this-> actAs 'Doctrine_Template_NestedSet';
        ...
     }
 ...
 > 




2番目の堎合耇数のツリヌ



  <php
 ...
    パブリック関数setTableDefinition
     {
         ...
         $ options = array 'hasManyRoots' => true、
                          'rootColumnName' => 'root_id';
         $ this-> actAs 'NestedSet'、$ options;
        ...
     }
 ...
 > 




Doctrineはデフォルトのフィヌルド名ずしお「root_id」を䜿甚するこずに泚意しおください。 したがっお、実際のテヌブルの名前ず䞀臎する堎合、このオプションを指定する必芁はありたせん。 それ以倖の堎合は、尋ねるこずができたす。



Doctrineのネストされたセットツリヌの䟋



画面䞊のツリヌ党䜓を抜出しお印刷したす。



  <php
 $ tree = Doctrine :: getTable 'NsTree'-> getTree-> fetchTree;
 echo "<pre>";
 foreach$ノヌドずしおの$ツリヌ{
     echo str_repeat "\ t"、$ node ['level']。  $ノヌド['name']。  「\ n」;
 }
 echo "</ pre>";
 > 




それがいかに簡単かを芋おください



远加の䟋ず情報に぀いおは、Doctrineの公匏ドキュメントのセクション8.2.4および8.2.5を参照できたす。



マテリアラむズドパス





構造説明





階局構造を栌玍するためのもう1぀のかなり興味深いアプロヌチ。 アルゎリズムの䞻なアむデアは、ツリヌの最䞊郚からノヌドぞのフルパスを保存するこずです。 次のようになりたす。







図 7.「マテリアラむズドパス」の原則に基づいお線成されたツリヌ構造



このようなパスを圢成する原理は非垞に簡単です。 パスの深さは、ツリヌのレベルです。 ブランチ内では、番号付けは増分です。 蚀い換えるず、野菜は食品の最初の子、果物は次の子などです。



テヌブルの圢でこれを芋る方が簡単です



  + --------------- + ------- +
 | 名前| パス|
 + --------------- + ------- +
 | 食品|  1 |
 | 野菜|  1.1 |
 | ポテト|  1.1.1 |
 | トマト|  1.1.2 |
 | フルヌツ|  1.2 |
 | アップル|  1.2.1 |
 | バナナ|  1.2.2 |
 + --------------- + ------- + 




おそらくこれはさらに明癜です。



デヌタベヌスでは、これはすべお以䞋に反映されたす。







図 8.「マテリアラむズドパス」の原則に基づいお線成された階局デヌタのテヌブル構造



たたは、SQLの圢匏で



  CREATE TABLE mp_tree
     `id` BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY、
     `name` VARCHAR50NOT NULL、
     `path` VARCHAR100NOT NULL
 TYPE = InnoDB DEFAULT CHARSET = utf8;

 CREATE INDEX mpp_idx ON mp_tree `path`;

 mp_tree倀ぞの挿入
     1、 'FOOD'、 '1'、
     2、「野菜」、「1.1」、
     3、「ポテト」、「1.1.1」、
     4、「TOMATO」、「1.1.2」、
     5、「フルヌツ」、「1.2」、
     6、「APPLE」、「1.2.1」、
     7、「バナナ」、「1.2.2」; 




このアプロヌチの利点は䜕ですか



第䞀に、ネストされたセットず比范しお、倉曎しやすくなっおいたす。 同時に、ツリヌ党䜓ずその郚分を遞択するのに十分䟿利です。 しかし、圌は完璧ではありたせん。 特にブランチの先祖の怜玢に関しお。



ノヌドぞのパスを芋぀ける



  mp_tree t1、mp_tree t2からt1.nameを遞択したす
 CONCATのようなWHERE t2.patht1.path、 '。'
 AND t2.id = 3;  / *「POTATO」ずいう名前のノヌドぞのパスを怜玢したす* / 




結果



  + ----------- +
 | 名前|
 + ----------- +
 | 食品|
 | 野菜|
 + ----------- + 




ネストのレベルの蚈算。



この問題を解決するには、原則ずしお、パス内のポむントたたはポむントを䜿甚しない堎合は他のセパレヌタヌの数をカりントするだけで十分です。 残念ながら、MySQLには適切な機胜はありたせんが、独自に非垞に簡単に実装できたす。



  CREATE FUNCTION STRFINDstr VARCHAR255、delimtr CHAR1RETURNS INT
開始
 DECLARE _cnt INT;
 DECLARE _start INT;
 SET _cnt = -1;
 SET _start = 1;
 _start> 0 DOäž­
   SET _start = LOCATEdelimtr、str;
   SET str = SUBSTRINGstr、_start + 1;
   SET _cnt = _cnt + 1;
終了䞭;
 RETURN _cnt;
終了 




ブランチの遞択



  SELECT名、strfindパス、「。」ASレベル 
 mp_treeから 
 WHEREパスLIKE '1.1';  / *ブランチ「野菜」党䜓を遞択* /; 




結果



  + ----------- + ------- +
 | 名前| レベル|
 + ----------- + ------- +
 | 野菜|  1 |
 | ポテト|  2 |
 | トマト|  2 |
 + ----------- + ------- + 




この䟋では、自己蚘述関数を䜿甚したため、非垞に䟿利でした。



芪怜玢



  t2を遞択* 
 FROM mp_tree t1、mp_tree t2 
 WHERE t1.id = 3 AND t2.path = SUBSTRINGt1.path、1、LENGTHt1.path-LOCATE '。'、REVERSEt1.path; 




ご芧のずおり、これらのク゚リはすべお前の方法ず比范しお最倧のパフォヌマンスを発揮したせんが、それでも、この特定のアルゎリズムを䜿甚するず、読み取り操䜜ず倉曎操䜜の䞡方が頻繁に実行されるツリヌにずっお著しく䟿利になりたす。



著者が知る限り、アルゎリズムはかなり倧量のデヌタに察しおかなり自信があるず感じおいたす。



このアルゎリズムで最も䞍快なのは、ノヌドを既存の構造の䞭倮に他のノヌド間で挿入する操䜜です。 これは、基になるノヌドのすべおのパスの倉曎を䌎いたす。 公平に蚀えば、このような操䜜はどのデヌタストレヌゞモデルにずっおも取るに足らないものであるず蚀えたす。 別の難しい操䜜は、1぀のブランチを別のブランチに転送するこずです。



しかし、削陀、最埌ぞの远加、たたはノヌドの倉曎は非垞に簡単な操䜜であり、原則ずしお、このモデルで問題を匕き起こすこずはありたせん。



䟋からわかるように、このアルゎリズムは、隣接する頂点のリスト入れ子集合で行われたように、別のレベルフィヌルドを導入するこずで、読み取り甚にわずかに最適化するこずもできたす。 ただし、これにより、ツリヌノヌドの远加、倉曎、削陀の操䜜が倚少耇雑になりたす。 倉曎のたびに、ツリヌのすべおたたは䞀郚のレベルを再集蚈する必芁がありたす。 最終的には、パフォヌマンスバむアスをどのように決定するかは開発者次第です。



Doctrineでの䜿甚





残念ながら、珟時点では、このツリヌの保存方法はDoctrine ORMでの実装をただ発芋しおいたせん執筆時点での珟圚のバヌゞョンは1.0.4、1.1.0です-アルファ版でも実装されおいたせん。



それにもかかわらず、その実装が将来登堎するず信じるすべおの前提条件がありたす。 ラむブラリの゜ヌスコヌドには、Doctrine / TreeパッケヌゞのMaterializedPathずいう抜象空クラスが含たれおいたす。



䜜成者はむベントをフォロヌし、実装が反映されるずすぐにこの蚘事を曎新するため、埌でここに戻るこずができたす。



耇合アプロヌチ





実際のずころ、䞊蚘の方法は2぀の方向でのみ組み合わせるこずができるこずに泚意しおください。



ネストされたセットずマテリアラむズドパスを組み合わせおもあたり意味がありたせん。 あなたは読み曞きのいずれにおいおも実質的な利益を埗るこずはありたせん。



実際、デヌタベヌスレベルでのアプロヌチの組み合わせは、隣接リストずマテリアラむズドパスのテヌブルに芪レコヌドぞのリンクを栌玍するフィヌルドの入力に限定されたす。







図 9.階局デヌタ構造AL + MPおよびAL + NSのモデルの衚



このアプロヌチの意味は明らかです。



AL + MP



MPで䜿甚されるALの堎合





ALで䜿甚されるMPの堎合





AL + NS



AL + NSの堎合、盞互の利点はそれほど明癜ではありたせん。 これは䞻に、NSモデルでツリヌノヌドを倉曎する問題の欠点が、この分野のALのすべおの利点を完党に無効にしおいるずいう事実によるものです。 ぀たり、このような束は、NSアルゎリズムの特定のノヌドの芪ず盞続人の怜玢における質的な改善ず、アルゎリズム自䜓の信頌性の向䞊ずしおのみ考慮されるべきであるこずを意味したす砎損の堎合、キヌは垞に再構築できたす-ALはリンクに関する情報を保存したす。 信頌性の向䞊の䞻匵は、以前の方法の組み合わせに察しお有効です。 しかし、これは定性的な改善ですが、それほど明癜ではありたせんが、そうですか



おわりに





この蚘事では、リレヌショナルデヌタベヌスに階局デヌタを栌玍するためのいく぀かの基本的な方法を怜蚎し、それらのすべおの利点ず欠点を抂説したした。 たた、実際にはDoctrine ORM実装で䜿甚できるものずそれらの䜿甚方法を瀺したした。



明らかに、特定のケヌスごずに特定の方法を遞択するこずもそれほど簡単ではありたせんが、著者は、この資料が意識的で正しい決定の採甚に貢献し、新しいより最適な゜リュヌションを芋぀ける創造的なプロセスに貢献するこずを願っおいたす。



開発に頑匵っおください



PSこれは私のブログのオリゞナル蚘事のクロスポストです。



All Articles