Mysql
 sql >> база данни >  >> RDS >> Mysql

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

Този въпрос често възниква не само за таблицата за затваряне, но и за други методи за съхранение на йерархични данни. Не е лесно в нито един от дизайните.

Решението, което измислих за Closure Table, включва едно допълнително присъединяване. Всеки възел в дървото се присъединява към веригата на своите предшественици, като заявка от типа "хлебни трохи". След това използвайте 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         |
+----+------------+--------+---------+-------------+

Предупреждения:

  • Стойностите на id трябва да имат еднаква дължина, тъй като сортирането „1,3“ и „1,6“ и „1327“ може да не даде реда, който възнамерявате. Но сортирането на "001,003" и "001,006" и "001,327" би могло. Така че или трябва да стартирате стойностите на идентификатора си от 1000000+, или да използвате ZEROFILL за предшественик и потомък в таблицата category_closure.
  • В това решение редът на показване зависи от числовия ред на идентификаторите на категорията. Този числов ред на стойностите на id може да не представлява реда, в който искате да покажете дървото. Или може да искате свободата да променяте реда на показване, независимо от стойностите на цифровия идентификатор. Или може да искате едни и същи данни за категорията да се показват в повече от едно дърво, всяко с различен ред на показване.
    Ако имате нужда от повече свобода, трябва да съхранявате стойностите на реда на сортиране отделно от идентификаторите и решението получава още по-сложно. Но в повечето проекти е приемливо да се използва пряк път, като се дава двойно задължение на идентификатора на категорията като ред на показване на дървото.

Отново вашия коментар:

Да, можете да съхраните „сортиране на брат и сестра“ като друга колона в таблицата за затваряне, след което да използвате тази стойност вместо ancestor за изграждане на низа за галета. Но ако направите това, в крайна сметка ще получите много излишни данни. Това означава, че даден предшественик се съхранява на няколко реда, по един за всеки път, слизащ от него. Така че трябва да съхранявате една и съща стойност за реда на сортиране на брат и сестра на всички тези редове, което създава риск от аномалия.

Алтернативата би била да създадете друга таблица, само с една ред на отделен предшественик в дървото и се присъединете към тази таблица, за да получите реда на брат и сестра.

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         |
+----+------------+--------+---------+-------------+



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Добавяне на връзка едно към много към самореферентен родител/дете

  2. Как да използвате „having“ с paginate в колоната на връзката в laravel 5

  3. Максимална дължина на MySQL и GROUP_CONCAT().

  4. Как да използвам mysqli_query() в PHP?

  5. sql, за да изберете топ 10 записа