sql >> Databáze >  >> RDS >> Mysql

Třídění podstromu v hierarchické datové struktuře uzavírací tabulky

Tato otázka se často objevuje nejen u uzavírací tabulky, ale také u jiných metod ukládání hierarchických dat. V žádném z provedení to není snadné.

Řešení, které jsem pro Closure Table vymyslel, zahrnuje jedno další spojení. Každý uzel ve stromu se připojí k řetězci svých předků jako dotaz typu „drobečková navigace“. Poté pomocí GROUP_CONCAT() sbalte drobečky do řetězce odděleného čárkami a seřaďte čísla ID podle hloubky ve stromu. Nyní máte řetězec, podle kterého můžete třídit.

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

Upozornění:

  • Hodnoty id by měly mít jednotnou délku, protože řazení „1,3“ a „1,6“ a „1 327“ nemusí poskytnout požadované pořadí. Ale třídění "001,003" a "001,006" a "001,327" by. Takže musíte buď začít s hodnotami id na 1000000+, nebo použít ZEROFILL pro předka a potomka v tabulce category_closure.
  • V tomto řešení závisí pořadí zobrazení na číselném pořadí ID kategorií. Toto číselné pořadí hodnot id nemusí představovat pořadí, ve kterém chcete strom zobrazit. Nebo můžete chtít svobodu změnit pořadí zobrazení bez ohledu na číselné hodnoty id. Nebo můžete chtít, aby se stejná data kategorií objevila ve více než jednom stromě, každý s jiným pořadím zobrazení.
    Pokud potřebujete větší volnost, musíte uložit hodnoty pořadí řazení odděleně od id a řešení získá ještě složitější. Ale ve většině projektů je přijatelné použít zkratku, která dává id kategorie dvojitou povinnost jako pořadí zobrazení stromu.

K vašemu komentáři:

Ano, můžete uložit „sourozenecké pořadí řazení“ jako další sloupec v uzavírací tabulce a pak použít tuto hodnotu místo ancestor k vytvoření strouhanky. Ale pokud to uděláte, skončíte se spoustou redundance dat. To znamená, že daný předek je uložen na více řádcích, jeden pro každou cestu z něj sestupující. Takže musíte uložit stejnou hodnotu pro pořadí řazení sourozenců na všech těchto řádcích, což vytváří riziko anomálie.

Alternativou by bylo vytvořit další tabulku, pouze s jednou řádek na odlišného předka ve stromu a připojte se k této tabulce, abyste získali pořadí sourozenců.

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. Java:com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException:Nelze vytvořit připojení k databázovému serveru

  2. Převést „datetime“ na „datetimeoffset“ v SQL Server (příklady T-SQL)

  3. Najděte maximální počet po sobě jdoucích let pro každé ID v tabulce (Oracle SQL)

  4. sql jdbc getgeneratedkeys vrátí id sloupce nenalezeno, typ sloupce neznámý