Reputation: 64196
How can I sort the records of a SELECT statement so that they represent a valid tree?
All of my attempts show sub-nodes nested under wrong parent nodes. What is the most reliable way to achieve this ordering?
Data
ID Parent ID Title
--------------------------------------------
0 NULL Root
1 0 Node A
2 0 Node B
3 1 Sub-Node C
4 1 Sub-Node D
5 3 Sub-Node E
Output
ID Parent ID Title
--------------------------------------------
0 NULL Root
1 0 Node A
3 1 Sub-Node C
5 3 Sub-Node E
4 1 Sub-Node D
2 0 Node B
Data Visualisation
Root
Node A
Sub-Node C
Sub-Node E
Sub-Node D
Node B
Upvotes: 3
Views: 22100
Reputation: 91902
You can use Nested Sets. Check out this article:
Managing Hierarchical Data in MySQL
The author describes a few different methods for building hierarchies in SQL, complete with example queries. It's a very good read about this subject!
Upvotes: 13
Reputation: 21
Here is another way to do your PHP function.
function buildTree() {
$data = array();
$pointers = array();
$sql = "SELECT ID,PARENT,TITLE FROM TREE ORDER BY TITLE ASC";
$res = $this->db->query($sql);
while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
if(!isset($pointers[$row['ID']])) {
$pointers[$row['ID']] = $row;
}
if(!empty($row['PARENT'])) {
if(!isset($pointers[$row['PARENT']])) {
$pointers[$row['PARENT']] = $row;
}
$pointers[$row['PARENT']][$row['ID']] = &$pointers[$row['ID']];
} else {
$data[$row['ID']] = &$pointers[$row['ID']]; // This is our top level
}
}
unset($pointers);
return $data;
}
Upvotes: 0
Reputation: 121
I just finished this recursive function, and thought it was an elegant way about the problem. Here's what I did once I made a basic SELECT mysql query:
function orderChildren($data){
$tree = array();
foreach($data as $value){
if($value['parent_id'] == null){ // Values without parents
$tree[$value['id']] = $this->goodParenting($value, $data);
}
}
return $tree;
}
private function goodParenting($parent, $childPool){
foreach($childPool as $child){
if($parent['id'] == $child['parent_id']){
$parent['children'][$child['id']] = $this->goodParenting($child, $childPool);
}
}
return $parent;
}
Upvotes: 0
Reputation: 64196
Following the advice of @Blindy I have implemented this sort with PHP. Here are the two functions that seem to solve this issue relatively easily.
protected function _sort_helper(&$input, &$output, $parent_id) {
foreach ($input as $key => $item)
if ($item->parent_id == $parent_id) {
$output[] = $item;
unset($input[$key]);
// Sort nested!!
$this->_sort_helper(&$input, &$output, $item->id);
}
}
protected function sort_items_into_tree($items) {
$tree = array();
$this->_sort_helper(&$items, &$tree, null);
return $tree;
}
I would be interested to hear if there is a simpler approach, but this does seem to work.
Upvotes: 5
Reputation:
MySQL has no support for recursive queries
You will need to self join the table as many times as the maximum hierarchy level is, but still it's pretty ugly to get one row for each hierarchy level that way.
See these posts for some ideas and examples:
Category Hierarchy (PHP/MySQL)
Upvotes: 4