php / Mysql最好的树结构
我必须build立一个树,其中将包含约300个节点。 树没有深度限制。 所以它可以有3或15个级别。 每个节点可以有无限数量的子节点。
优先考虑的是尽可能快地获得完整的树/子树,但是我也需要添加节点或移动节点,但不是经常这样做。
我想知道在数据库中存储树的最佳方式,如果可能的话,在php中检索数据的最佳方法。
您可以使用嵌套集模型,因为它产生非常有效的查询。 查看pipe理MySQL中的分层数据并阅读“ 嵌套集模型 ”部分。
如果您使用的是像Doctrine这样的ORM,它包含嵌套设置function 。
有些人可能很难掌握左右嵌套的概念。 我发现使用这些数字作为XML文档中打开/closures标签的行数的类比,人们发现它更容易掌握。
例如,从上面的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 | +-------------+----------------------+-----+-----+
如果您使用lft , rgt字段并将它们用作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>
通过这种方式来看,可以让一些人更容易地看到最终的嵌套集层次结构。 这也更清楚地说明了为什么这种方法提高了效率,因为它可以在不需要多个查询或连接的情况下select整个节点。
这是一个很棒的文章: 在MySQL中pipe理分层数据 。 我用了很久。
如果你有一些math能力,你可以真正理解为什么这么好!
<?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; } ?>