sql >> Database >  >> RDS >> Mysql

Een subboom sorteren in een hiërarchische gegevensstructuur van een afsluittabel

Deze vraag komt vaak voor, niet alleen voor Closure Table, maar ook voor andere methoden voor het opslaan van hiërarchische gegevens. Het is niet gemakkelijk in een van de ontwerpen.

De oplossing die ik heb bedacht voor Closure Table omvat één extra join. Elk knooppunt in de boom sluit zich aan bij de keten van zijn voorouders, zoals een "broodkruimel"-query. Gebruik vervolgens GROUP_CONCAT() om de broodkruimels samen te vouwen tot een door komma's gescheiden tekenreeks, waarbij de ID-nummers op diepte in de boom worden gesorteerd. Nu heb je een string waarop je kunt sorteren.

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

Waarschuwingen:

  • De id-waarden moeten een uniforme lengte hebben, omdat het sorteren van "1,3" en "1,6" en "1,327" mogelijk niet de gewenste volgorde geeft. Maar het sorteren van "001,003" en "001.006" en "001.327" zou dat wel doen. U moet dus ofwel uw ID-waarden beginnen bij 1000000+, of anders ZEROFILL gebruiken voor voorouder en afstammeling in de tabel category_closure.
  • In deze oplossing hangt de weergavevolgorde af van de numerieke volgorde van categorie-ID's. Die numerieke volgorde van id-waarden vertegenwoordigt mogelijk niet de volgorde waarin u de boom wilt weergeven. Of misschien wilt u de vrijheid om de weergavevolgorde te wijzigen, ongeacht de numerieke ID-waarden. Of u wilt misschien dat dezelfde categoriegegevens in meer dan één boomstructuur verschijnen, elk met een andere weergavevolgorde.
    Als u meer vrijheid nodig heeft, moet u de sorteervolgordewaarden apart van de id's opslaan, en de oplossing krijgt nog complexer. Maar in de meeste projecten is het acceptabel om een ​​snelkoppeling te gebruiken, waarbij de categorie-ID dubbel wordt gebruikt als de weergavevolgorde van de boomstructuur.

Opnieuw uw opmerking:

Ja, u kunt "sorteervolgorde voor broers en zussen" opslaan als een andere kolom in de afsluittabel en die waarde gebruiken in plaats van ancestor om de broodkruimelsreeks te bouwen. Maar als je dat doet, krijg je veel gegevensredundantie. Dat wil zeggen, een bepaalde voorouder wordt opgeslagen op meerdere rijen, één voor elk pad dat ervan afdaalt. U moet dus dezelfde waarde voor de sorteervolgorde van broers en zussen op al die rijen opslaan, wat het risico van een anomalie met zich meebrengt.

Het alternatief zou zijn om een ​​andere tabel te maken, met slechts één rij per afzonderlijke voorouder in de boom en voeg je bij die tabel om de volgorde van broers en zussen te krijgen.

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. Gegevens ophalen uit MySQL-database naar html-vervolgkeuzelijst

  2. Php - Uw PHP-installatie lijkt de MySQL-extensie te missen die vereist is voor WordPress

  3. Kan PostgreSQL niet verbinden met externe database met pgAdmin

  4. Tips voor het upgraden van MySQL 5.7 naar MySQL 8