Сортировка поддерева в иерархической структуре данных стола закрытия

Я хотел бы попросить, чтобы вы помогли мне с проблемой с сортировкой иерархической структуры данных, сохраненной как <прочный> стол закрытия .

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

Моя структура на основе Статья Билла Карвина о Столах Закрытия и некоторых других постах.

Вот моя структура базы данных MySQL с некоторыми ДЕМОНСТРАЦИОННЫМИ данными:

--
-- Table `category`
--

CREATE TABLE IF NOT EXISTS `category` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(100) COLLATE utf8_czech_ci NOT NULL,
  `active` tinyint(1) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;


INSERT INTO `category` (`id`, `name`, `active`) VALUES
(1, 'Cat 1', 1),
(2, 'Cat 2', 1),
(3, 'Cat  1.1', 1),
(4, 'Cat  1.1.1', 1),
(5, 'Cat 2.1', 1),
(6, 'Cat 1.2', 1),
(7, 'Cat 1.1.2', 1);

--
-- Table `category_closure`
--

CREATE TABLE IF NOT EXISTS `category_closure` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `ancestor` int(11) DEFAULT NULL,
  `descendant` int(11) DEFAULT NULL,
  `depth` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `fk_category_closure_ancestor_category_id` (`ancestor`),
  KEY `fk_category_closure_descendant_category_id` (`descendant`)
) ENGINE=InnoDB;

INSERT INTO `category_closure` (`id`, `ancestor`, `descendant`, `depth`) VALUES
(1, 1, 1, 0),
(2, 2, 2, 0),
(3, 3, 3, 0),
(4, 1, 3, 1),
(5, 4, 4, 0),
(7, 3, 4, 1),
(8, 1, 4, 2),
(10, 6, 6, 0),
(11, 1, 6, 1),
(12, 7, 7, 0),
(13, 3, 7, 1),
(14, 1, 7, 2),
(16, 5, 5, 0),
(17, 2, 5, 1);

Вот мой Запрос Select для одного дерева:

SELECT c2.*, cc2.ancestor AS `_parent`
FROM category AS c1
JOIN category_closure AS cc1 ON (cc1.ancestor = c1.id)
JOIN category AS c2 ON (cc1.descendant = c2.id)
LEFT OUTER JOIN category_closure AS cc2 ON (cc2.descendant = c2.id AND cc2.depth = 1)
WHERE c1.id = __ROOT__ AND c1.active = 1
ORDER BY cc1.depth

Для ДЕМОНСТРАЦИОННОГО случая с __ ROOT_ = 1, что вопрос добирается:

id  name        active     _parent
1   Cat 1       1          NULL
3   Cat 1.1     1          1
6   Cat 1.2     1          1
4   Cat 1.1.1   1          3
7   Cat 1.1.2   1          3

Но что, если я, например, должен изменить заказ Кэт 1.1 и Кэт 1.2 (согласно имени или некоторому таможенному заказу)?

Я видел некоторое решение для крошек (как сортировать крошками), но я не знаю, как произвести и изменить их.

12
nl ja de
+1 благодарит отправить типовой DDL и данные.
добавлено автор Bill Karwin, источник

1 ответы

Этот вопрос часто подходит не только для Стола Закрытия, но также и для других методов того, чтобы хранить иерархические данные. Это не легко ни в одном из проектов.

Решение, которое я предложил для Стола Закрытия, включает одно дополнительное соединение. Каждый узел в дереве соединяет с цепью его предков, как вопрос типа "крошек". Тогда используйте GROUP_CONCAT (), чтобы разрушиться крошки в отделенную от запятой последовательность, сортируя идентификационные номера глубиной в дереве. Теперь у вас есть последовательность, которой можно сортировать.

SELECT c2.*, cc2.ancestor AS `_parent`,
  GROUP_CONCAT(breadcrumb.ancestor ORDER BY breadcrumb.depth DESC) AS breadcrumbs
FROM category AS c1
JOIN category_closure AS cc1 ON (cc1.ancestor = c1.id)
JOIN category AS c2 ON (cc1.descendant = c2.id)
LEFT OUTER JOIN category_closure AS cc2 ON (cc2.descendant = c2.id AND cc2.depth = 1)
JOIN category_closure AS breadcrumb ON (cc1.descendant = breadcrumb.descendant)
WHERE c1.id = 1/*__ROOT__*/ AND c1.active = 1
GROUP BY cc1.descendant
ORDER BY breadcrumbs;

+----+------------+--------+---------+-------------+
| id | name       | active | _parent | breadcrumbs |
+----+------------+--------+---------+-------------+
|  1 | Cat 1      |      1 |    NULL | 1           |
|  3 | Cat  1.1   |      1 |       1 | 1,3         |
|  4 | Cat  1.1.1 |      1 |       3 | 1,3,4       |
|  7 | Cat 1.1.2  |      1 |       3 | 1,3,7       |
|  6 | Cat 1.2    |      1 |       1 | 1,6         |
+----+------------+--------+---------+-------------+

Протесты:

  • у значений идентификаторов должна быть однородная длина, потому что сортировка "1,3" и "1,6" и "1,327" не могла бы дать распоряжение, которое вы предназначаете. Но сортировка "001,003" и "001,006" и "001,327" была бы. Таким образом, или необходимо начать значения идентификаторов по телефону 1000000 +, или иначе использование ZEROFILL для предка и потомка в category_closure столе.
  • В этом решении заказ показа зависит от числового заказа id категории. Тот числовой заказ значений идентификаторов может не представлять заказ, вы хотите показать дерево. Или можно хотеть свободу изменить заказ показа независимо от числовых значений идентификаторов. Или можно хотеть, чтобы те же самые данные о категории появились больше чем в одном дереве, каждом с различным заказом показа.
    Если вам нужно больше свободы, необходимо сохранить ценности порядка сортировки отдельно из id, и решение становится еще более сложным. Но в большинстве проектов, приемлемо использовать короткий путь, давая двойную обязанность id категории как заказ показа дерева.

Ре ваш комментарий:

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

Альтернатива должна была бы составить другую таблицу, с только <ими> один ряд на отличного предка в дереве, и соединить с тем столом, чтобы получить заказ родного брата.

CREATE TABLE category_closure_order (
  ancestor INT PRIMARY KEY,
  sibling_order SMALLINT UNSIGNED NOT NULL DEFAULT 1
);

SELECT c2.*, cc2.ancestor AS `_parent`,
  GROUP_CONCAT(o.sibling_order ORDER BY breadcrumb.depth DESC) AS breadcrumbs
FROM category AS c1
JOIN category_closure AS cc1 ON (cc1.ancestor = c1.id)
JOIN category AS c2 ON (cc1.descendant = c2.id)
LEFT OUTER JOIN category_closure AS cc2 ON (cc2.descendant = c2.id AND cc2.depth = 1)
JOIN category_closure AS breadcrumb ON (cc1.descendant = breadcrumb.descendant)
JOIN category_closure_order AS o ON breadcrumb.ancestor = o.ancestor
WHERE c1.id = 1/*__ROOT__*/ AND c1.active = 1
GROUP BY cc1.descendant
ORDER BY breadcrumbs;

+----+------------+--------+---------+-------------+
| id | name       | active | _parent | breadcrumbs |
+----+------------+--------+---------+-------------+
|  1 | Cat 1      |      1 |    NULL | 1           |
|  3 | Cat  1.1   |      1 |       1 | 1,1         |
|  4 | Cat  1.1.1 |      1 |       3 | 1,1,1       |
|  7 | Cat 1.1.2  |      1 |       3 | 1,1,2       |
|  6 | Cat 1.2    |      1 |       1 | 1,2         |
+----+------------+--------+---------+-------------+
15
добавлено
@TheSmileyCoder, потому что вам, возможно, понадобилось бы больше чем одно дерево, содержащее некоторые из тех же самых категорий в различном заказе.
добавлено автор Bill Karwin, источник
Большое спасибо г-н Карвин, поэтому когда я нуждаюсь в большей свободе на некотором уровне дерева и создаю таможенный заказ там (чтобы установить порядок пунктов меню, у которых есть тот же самый родитель), тогда я могу добавить sth как "та же самая приоритетная колонка" уровня, или это - плохая идея?
добавлено автор JCZ, источник
Большое спасибо г-н Карвин, я имею (надейтесь, что успешно), осуществил выбор с третьим столом. Вы великие.
добавлено автор JCZ, источник
Любопытный, почему бы не сохранить порядок сортировки в столе Категории, тот путь никакой третий стол не требуется? Когда I' ve, сделанный, сортируя в наследственном дереве, I' ve всегда использовал двойное, так, чтобы, когда новый узел должен быть вставлен между узлами 1 и 2, я мог дать ему sortValue 1.5
добавлено автор TheSmileyCoder, источник
@BillKarwin Да, я вижу. Я предполагаю в работе I' ve, сделанный, у меня только когда-либо должен был или быть таможенный вид или вид данными (такой как алфавитный на имени). Я действительно полагаю, что предпочел бы иметь его в "просто" 2 столах метода закрытия, если, конечно, я не знал, что должен буду поддержать многократные таможенные виды. Пища для размышления так или иначе, спасибо.
добавлено автор TheSmileyCoder, источник
DBA - русскоговорящее сообщество
DBA - русскоговорящее сообщество
1 345 участник(ов)

Общаемся и обсуждаем темы, посвященные DBA, PostgreSQL, Redis, MongoDB, MySQL, neo4j, riak и т.д. См. также: @devops_ru, @kubernetes_ru, @docker_ru, @nodejs_ru Рекомендуем сразу отключить уведомления, чтобы пребывание здесь было полезным и комфортным.

MySQL
MySQL
995 участник(ов)

The group is about MySQL. For code use hastebin.com. Admin: @smlkw