web-dev-qa-db-ja.com

PHP / Mysqlの最高のツリー構造

内部に約300のノードを含むツリーを作成する必要があります。ツリーには深さの制限はありません。したがって、3レベルまたは15レベルを持つことができます。各ノードは無制限の数の子を持つことができます。

優先事項は、完全なツリー/サブツリーをできるだけ早く取得することですが、ノードを追加したりノードを移動したりする必要がありますが、それほど頻繁ではありません。

私は、ツリーをデータベースに保存する最善の方法と、可能であればphpでデータを取得する最善の方法を知りたいです。

21
Marm

Nested Set Modelを使用すると、非常に効率的なクエリを生成できます。 MySQLでの階層データの管理 を確認し、Nested Set Modelというセクションを読んでください。

DoctrineのようなORMを使用している場合、それは ネストされたセット機能を含む です。

leftrightの入れ子集合の概念を理解するのは難しい場合があります。これらの数値をXMLドキュメント内の開始/終了タグの行番号の類推から、人々はそれを理解するのがより簡単であると感じます。

たとえば、上のMySQLリンクからのデータの例を見てみましょう。

+-------------+----------------------+-----+-----+
| category_id | name                 | lft | rgt |
+-------------+----------------------+-----+-----+
|           1 | ELECTRONICS          |   1 |  20 |
|           2 | TELEVISIONS          |   2 |   9 |
|           3 | TUBE                 |   3 |   4 |
|           4 | LCD                  |   5 |   6 |
|           5 | PLASMA               |   7 |   8 |
|           6 | PORTABLE ELECTRONICS |  10 |  19 |
|           7 | MP3 PLAYERS          |  11 |  14 |
|           8 | FLASH                |  12 |  13 |
|           9 | CD PLAYERS           |  15 |  16 |
|          10 | 2 WAY RADIOS         |  17 |  18 |
+-------------+----------------------+-----+-----+

lftrgtフィールドを取得して、XMLドキュメントの行番号として使用すると、次のようになります。

1. <electronics>
2.    <televisions>
3.        <tube>
4.        </tube>
5.        <lcd>
6.        </lcd>
7.        <plasma>  
8.        </plasma> 
9.     </televisions>
10.    <portable electronics>
11.        <mp3 players>
12.            <flash>
13.            </flash>
14.        </mp3 players>
15.        <cd players>
16.        </cd players>
17.        <2 way radios>
18.        </2 way radios>
19.    </portable electronics>
20. </electronics>

これをこの方法で見ると、結果のネストされたセット階層を視覚化するのがはるかに簡単になります。また、複数のクエリや結合を必要とせずにノード全体を選択できるため、このアプローチによって効率が向上する理由も明らかになります。

72
webbiedave

これは素晴らしい記事です MySQLでの階層データの管理 。長い間使っていました。

いくつかの数学的な機能があれば、それが非常に優れている理由を本当に理解できます!

7
Marcin
            <?php

            $Host = "localhost";
            //Database user name.   
            $login = "root";
            //Database Password.
            $dbpass = "";
            $dbname = "abc";
            $PDO = new PDO("mysql:Host=localhost;dbname=$dbname", "$login", "$dbpass");
            $rows = array();
            $sql = 'SELECT id, parent_id, name FROM employee';
            $query = $PDO->prepare($sql);
            $query->execute();
            $rows = array();

                if (!$query)
                {
                    $error = 'Error fetching page structure, for nav menu generation.';
                    exit();
                }

            while($row = $query->fetch(PDO::FETCH_ASSOC)){
                if( strcasecmp($row['parent_id'],'null') === 0 || empty($row['parent_id']) ) {
                     $row['parent_id'] = null;
                }

                $rows[] = $row;
            }


            // covert raw result set to tree
            $menu = convertAdjacencyListToTree(null,$rows,'id','parent_id','links');
            // echo '<pre>',print_r($menu),'</pre>';

            // display menu
            echo themeMenu($menu,1);

            /*
            * ------------------------------------------------------------------------------------
            * Utility functions
            * ------------------------------------------------------------------------------------
            */

            /*
            * Convert adjacency list to hierarchical tree
            *
            * @param value of root level parent most likely null or 0
            * @param array result
            * @param str name of primary key column
            * @param str name of parent_id column - most likely parent_id
            * @param str name of index that children will reside ie. children, etc
            * @return array tree
            */
            function convertAdjacencyListToTree($intParentId,&$arrRows,$strIdField,$strParentsIdField,$strNameResolution) {

                $arrChildren = array();

                for($i=0;$i<count($arrRows);$i++) {
                    if($intParentId === $arrRows[$i][$strParentsIdField]) {
                        $arrChildren = array_merge($arrChildren,array_splice($arrRows,$i--,1));
                    }
                }

                $intChildren = count($arrChildren);
                if($intChildren != 0) {
                    for($i=0;$i<$intChildren;$i++) {
                        $arrChildren[$i][$strNameResolution] = convertAdjacencyListToTree($arrChildren[$i][$strIdField],$arrRows,$strIdField,$strParentsIdField,$strNameResolution);
                    }
                }

                return $arrChildren;

            }

            /*
            * Theme menu
            *
            * @param array menu
            * @param runner (depth)
            * @return str themed menu
            */
            function themeMenu($menu,$runner) {

                $out = '';

                if(empty($menu)) {
                    return $out;
                }

                $out.='<ul>';
                foreach($menu as $link) {
                    $out.= sprintf(
                        '<li class="depth-%u">%s%s</li>'
                        ,$runner
                        ,$link['name']
                        ,themeMenu($link['links'],($runner+1))
                    );
                }

                $out.='</ul>';
                return $out;

            }

            ?>
0
Gaurav