sql >> Databasteknik >  >> RDS >> Mysql

Sortera ett underträd i en hierarkisk datastruktur för stängningstabellen

Den här frågan dyker upp ofta inte bara för Closure Table utan även för andra metoder för att lagra hierarkiska data. Det är inte lätt i någon av designerna.

Lösningen jag har kommit fram till för Closure Table innebär ytterligare en koppling. Varje nod i trädet ansluter till kedjan av sina förfäder, som en fråga av typen "brödsmulor". Använd sedan GROUP_CONCAT() för att kollapsa brödsmulorna till en kommaseparerad sträng, sortera id-numren efter djup i trädet. Nu har du en sträng som du kan sortera efter.

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

Varningar:

  • Id-värdena bör ha enhetlig längd, eftersom sortering av "1,3" och "1,6" och "1 327" kanske inte ger den ordning du tänkt dig. Men att sortera "001 003" och "001 006" och "001 327" skulle göra det. Så du måste antingen börja dina id-värden på 1000000+, eller så måste du använda ZEROFILL för förfader och avkomling i tabellen kategori_avslutning.
  • I den här lösningen beror visningsordningen på den numeriska ordningen för kategori-id:n. Den numeriska ordningen av id-värden kanske inte representerar den ordning du vill visa trädet. Eller så kanske du vill ha friheten att ändra visningsordningen oavsett de numeriska id-värdena. Eller så kanske du vill att samma kategoridata ska visas i mer än ett träd, vart och ett med olika visningsordning.
    Om du behöver mer frihet måste du lagra sorteringsordningsvärdena separat från id:t, och lösningen får ännu mer komplex. Men i de flesta projekt är det acceptabelt att använda en genväg, vilket ger kategori-id:t dubbel funktion som trädvisningsordningen.

Angående din kommentar:

Ja, du kan lagra "sorteringsordning för syskon" som en annan kolumn i stängningstabellen och sedan använda det värdet istället för ancestor att bygga ströbrödssnöret. Men om du gör det får du mycket dataredundans. Det vill säga, en given förfader lagras på flera rader, en för varje väg som går ner från den. Så du måste lagra samma värde för syskonsortering på alla dessa rader, vilket skapar risk för en anomali.

Alternativet skulle vara att skapa en annan tabell, med bara en rad per distinkt förfader i trädet och gå med i den tabellen för att få syskonordningen.

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. SQL Server:Hur använder man UNION med två frågor som BÅDA har en WHERE-klausul?

  2. Välj efter månad i ett fält

  3. välj count(*) från tabellen över mysql i php

  4. Hur man minimerar RPO för dina PostgreSQL-databaser med hjälp av punktåterställning