Упорядочивание результатов MySQL для подчинения родительскому / дочернему элементу в оглавлении

#php #mysql #hierarchical-data #tableofcontents

#php #mysql #иерархический-данные #tableofcontents

Вопрос:

У меня есть таблица для оглавления, которая выглядит следующим образом:

  • ID
  • Parent_ID
  • Глава
  • Display_Order

Следовательно, каждая строка является заголовком главы, но внутри глав могут быть главы внутри глав, внутри глав. Поэтому приведенная выше таблица позволяет мне поддерживать эти отношения.

Если у главы нет родительского элемента, т. Е. Она не является подразделом какой-либо другой главы, Parent_ID равен ‘Null’. Если у главы есть родительский элемент, его Parent_ID устанавливается на идентификатор родительской главы.

Поскольку в главе может быть несколько подразделов, порядок этих подразделов управляется с помощью столбца Display_Order; 1 является первым и т. Д.

Может ли кто-нибудь предложить аккуратный SQL-запрос, который позволил бы мне выбрать всю таблицу и получить результат, который выполняет вышеуказанное? По сути, я ищу набор результатов, который отражает фактическую иерархию глав. Оглавление ASCII ниже!

 Chapter
-- Chapter
---- Chapter
---- Chapter
---- Chapter
-- Chapter
---- Chapter
---- Chapter
Chapter
Chapter
  

и т.д.

Комментарии:

1. Известно ли количество уровней?

2. Один запрос сможет вернуть все результаты только в Display_Order . Тогда вам просто нужно будет просмотреть результаты и идентифицировать их в зависимости от их родителя? Если вы не можете заставить это работать, возможно, посмотрите на модель списка смежности: sitepoint.com/hierarchical-data-database .

3. Еще лучше был бы метод «модифицированного обхода дерева предварительного заказа», упомянутый на следующей странице этой статьи. Этот метод позволяет ему получить полную иерархию в одном простом запросе. sitepoint.com/hierarchical-data-database-2

4. @Lumbendil количество уровней неизвестно, и оно также не может быть исправлено, поскольку оглавление должно быть очень гибким.

Ответ №1:

Вы не можете сделать это только с SQL-запросами (по крайней мере, в MySQL). Один из подходов с SQL и PHP заключается в следующем:

 SELECT id, IFNULL(parent_id, 0) AS parentid, chapter FROM toc ORDER BY parentid, display_order
  

Затем вы считываете этот набор строк в массив $ a следующим образом:

 while ($row = mysql_fetch_array($result)) {
    $a[$row['id']]['name'] = $row['chapter'];
    $a[$row['parentid']]['children'][] = $row['id'];
}
  

Это создаст фиктивный элемент с первым индексом 0.

Небольшой пример функции для печати отступа для заданного уровня (вы можете использовать CSS с заполнением вместо или любым другим способом для создания отступа):

 function printIndent($level = 0) {
    for ($j = 0; $j <= $level; $j  ) echo 'amp;nbsp';
}
  

Затем вы создаете рекурсивную функцию printTree, которая выводит дерево:

 function printTree($key = 0, $level = 0) {
    if ($key > 0) {
         printIndent($level);
         echo $a[$key]['name'];
    }
    if (count($a[$key]['children'])
        foreach ($a[$key]['children'] as $child)
             printTree($child, $level   1);
}
  

и вы вызываете его один раз с:

 printTree();
  

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

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

Комментарии:

1. Забыл добавить, что очень важно иметь именно этот ПОРЯДОК SQL-запроса, поскольку способ генерации массива зависит от этого: родители должны быть повторены перед их дочерними элементами. И, конечно же, он учитывает порядок отображения дочерних элементов одного и того же родителя.

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