Сортировка дочерних элементов после их родительского элемента

#sql

#sql

Вопрос:

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

 id -- name -- parent_id
  

предполагая, что образец данных типа

 id - name - parent_id
1 test1  null
2 test2  null
3 test3  null
4 test4  1
5 test5  4
6 test6  2
7 test7  1
  

Я изо всех сил пытаюсь придумать sql-запрос, который вернет набор записей в следующем порядке

 id - name - parent_id
1 test1  null
4 test4  1
5 test5  4
7 test7  1
2 test2  null
6 test6  2
3 test3  null
  

В основном дочерние элементы возвращаются после их родительского элемента.

———————— РЕШАЕТСЯ С ПОМОЩЬЮ LINQ / рекурсии в коде ————————-

Не совсем sql-решение, но в конечном итоге оно работает.

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

1. Я не думаю, что есть достаточная спецификация. Кажется произвольным, почему test1 был родительским раньше test2 , или test3 если уж на то пошло.

2. Возможно, я упускаю суть. Но я не вижу связи между данными выборки и порядком ожидаемых данных. (Уже поздно, поэтому, если я упускаю очевидное, я приношу извинения)

3. test1 должен быть перед test4, если он хочет, чтобы он был упорядочен в соответствии с родительским отношением, из этого следует, что test2, являющийся следующим идентификатором в упорядоченном списке, будет следующим родительским элементом в результатах

4. С помощью одного запроса? нет, вы не можете.

5. Это иерархический запрос, и такие вещи, как известно, трудно кодировать — будь то в SQL или любой другой системе. Существуют (совместимые со стандартом) диалекты SQL, которые поддерживают предложение WITH и предложение WITH RECURSIVE. Существуют другие диалекты, которые поддерживают предложения CONNECT BY . Работа с 3-уровневой иерархией, как в вопросе, не слишком сложна; расширение ее до 4 уровней также не так уж плохо. Одна из трудностей заключается в том, чтобы знать, сколько уровней вам нужно поддерживать, и писать SQL для каждого такого уровня — отсюда необходимость в рекурсивных решениях.

Ответ №1:

Основываясь на том, что вы пытаетесь сделать с запросом, вам не нужно сортировать его таким образом. Вам просто нужно убедиться, что сначала созданы родительские элементы. Итак, запустите свой запрос, отсортированный по родительскому идентификатору, поместите результат в массив и выполните цикл по этому массиву. На каждой итерации выполняйте проверку, чтобы убедиться, что parent существует, если у него есть parent. Если родительский элемент не существует, просто переместите этот элемент в конец массива и перейдите к следующему на данный момент у вас должно получиться всего несколько перемещаемых обращений, чтобы он оставался достаточно эффективным.

Ответ №2:

Что я всегда делал в прошлом, так это разбивал базу данных на следующие части (хотя я не лучший специалист по SQL, поэтому для вас могут быть какие-то другие решения).

 categories
 - category_id  |  int(11)  | Primary Key / Auto_Increment
 ..
 ..

sub_categories
 - sub_category_id  |  int(11)  |  Primary Key / Auto_Increment
 - category_id      |  int(11)  |  Foreign Key to categories table
 ..
 ..
  

Ответ №3:

Вот что я бы сделал:

 SELECT id, name, parent_id, (CASE WHEN COALESCE(parentid,0)=0 THEN id ELSE (parentid   '.'   id)) as orderid
FROM table
ORDER BY (CASE WHEN COALESCE(parentid,0)=0 THEN id ELSE (parentid   '.'   id))
  

Это должно создать новый столбец с именем orderid, в котором родительский идентификатор совпадает с идентификатором (1.4, 4.5 и т.д.) Для столбцов, в которых родительский идентификатор равен null, он будет указывать только идентификатор. Таким образом, вы получите порядок как 1, 1.4, 4, 4.5 и т.д.

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

Ответ №4:

Приведенный ниже запрос выполняется путем добавления дополнительного order_parent столбца, который содержит либо родительский идентификатор, либо идентификатор строки, в зависимости от того, является ли он родительским. Затем он просто сортирует сначала по order_parent идентификатору, чтобы сгруппировать их вместе, затем по parent_id для сортировки null s (фактических родителей) сверху.

Две вещи:

  1. Здесь есть еще один столбец, который вы изначально хотели, поэтому просто игнорируйте его.
  2. В случае, если ваша база данных возвращает nulls parent_id последним, добавьте DESC .

Кстати, хороший вопрос!

 SELECT   id,
         name,
         parent_id,
         (
             case
             when parent_id is null then id
             else parent_id
             end
         ) AS order_parent
FROM     myTable
ORDER BY order_parent, parent_id
  

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

1. Вы выполняли этот запрос? Дочерние элементы сортируются, но внуки (id = 5) заканчиваются последними

2. Ах, внуки. Вполне. Я этого не заметил.

3. да, я пытался запустить его. Похоже, что это вот-вот сработает. но на самом деле это не работает. Для простоты (чтобы избежать нулей) я скопировал идентификаторы в поля родительского идентификатора для узлов, у которых нет родительского элемента.

4. Я не могу придумать способ согласовать это, чтобы работать на нескольких уровнях поколений. Даже если у вас есть корневой родительский элемент в order_parent , у вас не было бы чего сортировать, чтобы получить внуков в правильном порядке.

5. тем не менее, ценю усилия. Вероятно, в конечном итоге я сделаю это с помощью кода. 🙁