Рекурсивные категории с одним запросом? - PullRequest
15 голосов
/ 25 июня 2010

У меня есть веб-сайт со статьями и разделами, каждый раздел может иметь родительский раздел, например, как:

subject 1
 -subject 2 
 --subject 3
 -subject 4
 --subject 5
 --subject 6
 ---subject 7
subject 8
subject 9

и т. Д.

Теперь я хочучтобы получить их рекурсивно, какой самый эффективный способ сделать это через php и mysql?

Tnx в продвинутом.

Ответы [ 11 ]

27 голосов
/ 25 июня 2010

Если дерево не слишком большое, вы можете просто построить дерево в PHP, используя несколько умных ссылок.

$nodeList = array();
$tree     = array();

$query = mysql_query("SELECT category_id, name, parent FROM categories ORDER BY parent");
while($row = mysql_fetch_assoc($query)){
    $nodeList[$row['category_id']] = array_merge($row, array('children' => array()));
}
mysql_free_result($query);

foreach ($nodeList as $nodeId => &$node) {
    if (!$node['parent'] || !array_key_exists($node['parent'], $nodeList)) {
        $tree[] = &$node;
    } else {
        $nodeList[$node['parent']]['children'][] = &$node;
    }
}
unset($node);
unset($nodeList);

Это даст вам древовидную структуру в $tree с дочерними элементами всоответствующий children -слот.

Мы сделали это с довольно большими деревьями (>> 1000 элементов), и это очень стабильно и намного быстрее, чем делать рекурсивные запросы в MySQL.

10 голосов
/ 25 июня 2010

Это зависит от того, как вы сохранили свои данные.На MySQL.com есть хорошая статья под названием Управление иерархическими данными в MySQL , в которой говорится об этом.

3 голосов
/ 25 июня 2010

Ну, вы можете выбрать все категории в массиве всего за один запрос, как вы знаете:

$query = "SELECT `name`,`id` from `table`";

Имея это в массиве, вы можете построить дерево с несколькими вложенными циклами.Это не будет быстро, но просто и быстрее, чем использование рекурсивных запросов.Также вы можете кэшировать построенное дерево, и вам не нужно каждый раз его перестраивать.

2 голосов
/ 12 мая 2011

Мой также использует рекурсию с одним запросом ...

Рекурсивный метод хранения иерархических данных без многократных обращений к базе данных

Я хотел использовать рекурсию также из-за ее простоты, но, как и вы, я хотел снять накладные расходы с рекурсивного запроса. Моя логика гласит, что вы просто перемещаете нагрузку из базы данных в память в зависимости от размера результата вашей базы данных, но я не проводил никаких реальных испытаний на масштабируемость, поэтому я не знаю, какое влияние это оказывает нерекурсивные методы.

2 голосов
/ 25 июня 2010

Вы могли бы взглянуть на эту тему: как получить иерархическое меню из mysql , который был открыт вчера и примерно такой же.

1 голос
/ 25 июня 2010

Я не могу гарантировать, что не допустил синтаксических ошибок, но это должно работать с одним запросом.

class menuSystem{ 
var $menu;
var $db; #this variable is my db class assigned from the construct, I havent written the construct in, I can if you need it
function startNav(){
    $this->db->runQuery("select * from table order by parent asc");
    $menu = array(0 => array('children' => array()));
    while ($data = $this->db->fetchArray()) {
      $menu[$data['category_id']] = $data;
      $menu[(is_null($data['parent']) ? '0' : $data['parent'] )]['children'][] = $data['category_id'];
    }
    $this->menu = $menu;
    $nav = '<ul>';
    foreach($menu[0]['children'] as $child_id) {
      $nav .= $this->makeNav($menu[$child_id]);
    }
    $nav .= '</ul>';
}

function makeNav($menu){
   $nav_one = '<li>'."\n\t".'<a href="#">'$menu['name'].'</a>';
    if(isset($menu['children']) && !empty($menu['children'])) {
      $nav_one .= "<ul>\n";
      foreach($menu['children'] as $child_id) {
        $nav_one .= $this->makeNav($this->menu[$child_id]);
      }
      $nav_one .= "</ul>\n";
    }
    $nav_one .= "</li>\n";
return $nav_one;
}

}

РЕДАКТИРОВАТЬ: извините, я использую это в своем коде в качестве класса и думал, что мне удалось вывести его из класса для вас, но забыл, что мне нужно $ this-> menu

ОБНОВЛЕНИЕ: Я думаю, что ниже не из класса, извините за такой длинный ответ

$result = mysql_query("select * from table order by parent_id asc");
$menu = array(0 => array('children' => array()));
while ($data = mysql_fetch_array($result)) {
  $menu[$data['category_id']] = $data;
  $menu[(is_null($data['parent_id']) ? '0' : $data['parent_id'] )]['children'][] = $data['category_id'];
}
$global_menu = $menu;
$nav = '<ul>';
foreach($menu[0]['children'] as $child_id) {
  $nav .= makeNav($menu[$child_id]);
}
$nav .= '</ul>';

function makeNav($menu) {
  global $global_menu;
  $nav_one = '<li>'."\n\t".'<a href="#">' . $menu['name'].'</a>';
  if(isset($menu['children']) && !empty($menu['children'])) {
    $nav_one .= "<ul>\n";
    foreach($menu['children'] as $child_id) {
      $nav_one .= makeNav($global_menu[$child_id]);
    }
    $nav_one .= "</ul>\n";
  }
  $nav_one .= "</li>\n";
  return $nav_one;
}

Надеюсь, это поможет

Люк

0 голосов
/ 14 февраля 2018

Я сделал запрос для вас.Это даст вам рекурсивную категорию с одним запросом:

SELECT id,NAME,'' AS subName,'' AS subsubName,'' AS subsubsubName FROM app_category WHERE Parent_id=0
UNION 
SELECT b.id,a.name,b.name AS subName,'' AS subsubName,'' AS subsubsubName FROM app_category AS a LEFT JOIN app_category AS b ON b.parent_id=a.id WHERE a.Parent_id=0 AND b.name IS NOT NULL 
UNION 
SELECT c.id,a.name,b.name AS subName,c.name AS subsubName,'' AS subsubsubName FROM app_category AS a LEFT JOIN app_category AS b ON b.parent_id=a.id LEFT JOIN app_category AS c ON c.parent_id=b.id WHERE a.Parent_id=0 AND c.name IS NOT NULL 
UNION 
SELECT d.id,a.name,b.name AS subName,c.name AS subsubName,d.name AS subsubsubName FROM app_category AS a LEFT JOIN app_category AS b ON b.parent_id=a.id LEFT JOIN app_category AS c ON c.parent_id=b.id LEFT JOIN app_category AS d ON d.parent_id=c.id WHERE a.Parent_id=0 AND d.name IS NOT NULL 
ORDER BY NAME,subName,subsubName,subsubsubName 

Вот скрипка .

0 голосов
/ 30 июля 2010

У меня есть хорошее решение этой проблемы.

Он не использует рекурсию .И это требует один запрос к базе данных.

Я только что разместил код в своем ответе на аналогичный вопрос здесь:

https://stackoverflow.com/questions/2871861#3368622

Спасибо.

0 голосов
/ 25 июня 2010

Из вашего примера к каждой категории сохраните полный путь в другом поле:
1 - 1
2 - 1,2
3 - 1.2.3
4 - 1,4
5 - 1.4.5
6 - 1.4.6
7 - 1.4.6.7
8 - 8
9 - 9

, а затем просто запросите ORDER BY в этом поле

0 голосов
/ 25 июня 2010

, если предположить, что ваша таблица имеет id , id_parrent и поля имени

function tree($id)
{
    $query = "SELECT `name`,`id` from `table` WHERE `id_parrent` = '$id'";
    $result = mysql_query($query);
     if(mysql_num_rows($result) != 0)
       {
            echo "<ul>";
            while($row = mysql_fetch_array($result))
            {             
                 echo "<li>",$row[name],"</li>";
                 tree($row[id]);
            }
            echo "</ul>";
       }
}

таким образом, вы получите все дерево

category1
       category1_1
       category1_2
              category1_2_1
              category1_2_2
       category1_3
...........................
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...