Как я могу сортировать записи инструкции SELECT, чтобы они представляли действительное дерево?
Все мои попытки показывают под-узлы, вложенные под неправильные родительские узлы. Какой самый надежный способ добиться этого заказа?
Данные
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
Вывод
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
Визуализация данных
Root Node A Sub-Node C Sub-Node E Sub-Node D Node B
Вы можете использовать вложенные наборы. Ознакомьтесь с этой статьей:
Управление иерархическими данными в MySQL
Автор описывает несколько разных методов построения иерархии в SQL, в комплекте с примерами запросов. Это очень хорошо читать об этом предмете!
Следуя совету @Blindy, я реализовал этот вид с PHP. Вот две функции, которые, похоже, довольно легко решают эту проблему.
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; }
с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; }
Мне было бы интересно услышать, есть ли более простой подход, но, похоже, это работает.
MySQL не поддерживает рекурсивные запросы
Вам нужно будет присоединяться к таблице столько раз, сколько максимальный уровень иерархии, но все же довольно уродливо получить одну строку для каждого уровня иерархии таким образом.
См. Эти сообщения для некоторых идей и примеров:
Иерархия категорий (PHP / MySQL)
как мы можем написать mysql-запрос, где у родительского идентификатора есть дочерний идентификатор, а в следующий раз дочерний id – родительский идентификатор, как я могу это сделать?
Я только что закончил эту рекурсивную функцию и подумал, что это изящный способ решить эту проблему. Вот что я сделал, как только я сделал базовый запрос SELECT mysql:
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; }
Вот еще один способ сделать вашу функцию PHP.
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; }
этот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; }
не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; }