Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Welches hierarchische Modell soll ich verwenden? Angrenzend, verschachtelt oder aufgezählt?

Typischerweise gibt es drei Arten von Abfragen in den Hierarchien, die Probleme verursachen:

  1. Alle Vorfahren zurückgeben
  2. Alle Nachkommen zurückgeben
  3. Alle untergeordneten Elemente (direkte Nachkommen) zurückgeben.

Hier ist eine kleine Tabelle, die die Leistung verschiedener Methoden in MySQL zeigt :

                        Ancestors  Descendants  Children        Maintainability InnoDB
Adjacency list          Good       Decent       Excellent       Easy            Yes
Nested sets (classic)   Poor       Excellent    Poor/Excellent  Very hard       Yes
Nested sets (spatial)   Excellent  Very good    Poor/Excellent  Very hard       No
Materialized path       Excellent  Very good    Poor/Excellent  Hard            Yes

Bei children , poor/excellent bedeutet, dass die Antwort davon abhängt, ob Sie die Methode mit Adjazenzliste mischen, d.h. e. Speichern der parentID in jedem Datensatz.

Für Ihre Aufgabe benötigen Sie alle drei Abfragen:

  1. Alle Vorfahren, um die Erde/UK/Devon-Sache zu zeigen
  2. Alle Kinder zeigen "Reiseziele in Europa" (die Artikel)
  3. Alle Nachkommen zeigen "Reiseziele in Europa" (die Grafen)

Ich würde materialisierte Wege gehen, da sich diese Art von Hierarchie selten ändert (nur im Falle von Krieg, Revolte etc).

Erstellen Sie eine varchar-Spalte namens path , indizieren Sie es und füllen Sie es wie folgt mit dem Wert:

1:234:6345:45454:

wobei die Zahlen Primärschlüssel der entsprechenden Eltern sind, in der richtigen Reihenfolge (1 für Europa 234 für UK usw.)

Sie benötigen außerdem eine Tabelle namens levels Zahlen von 1 zu halten bis 20 (oder welche maximale Verschachtelungsebene Sie möchten).

So wählen Sie alle Vorfahren aus:

SELECT   pa.*
FROM     places p
JOIN     levels l
ON       SUBSTRING_INDEX(p.path, ':', l.level) <> p.path
JOIN     places pa
ON       pa.path = CONCAT(SUBSTRING_INDEX(p.path, ':', l.level), ':') 
WHERE    p.id = @id_of_place_in_devon

So wählen Sie alle untergeordneten Elemente und die Anzahl der darin enthaltenen Orte aus:

SELECT  pc.*, COUNT(pp.id)
FROM    places p
JOIN    places pc
ON      pc.parentId = p.id
JOIN    places pp
ON      pp.path BETWEEN pc.path AND CONCAT(pc.path, ':')
        AND pp.id NOT IN
        (
        SELECT  parentId
        FROM    places
        )
WHERE   p.id = @id_of_europe
GROUP BY
        pc.id