sql >> Database >  >> RDS >> Mysql

Welk hiërarchisch model moet ik gebruiken? Aangrenzend, genest of opgesomd?

Meestal zijn er drie soorten zoekopdrachten in de hiërarchieën die problemen veroorzaken:

  1. Geef alle voorouders terug
  2. Geef alle nakomelingen terug
  3. Geef alle kinderen terug (directe afstammelingen).

Hier is een kleine tabel die de prestaties van verschillende methoden in MySQL laat zien :

                        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

In children , poor/excellent betekent dat het antwoord afhangt van of u de methode mengt met de lijst met aangrenzende gebieden, i. e. opslaan van de parentID in elk record.

Voor je taak heb je alle drie de vragen nodig:

  1. Alle voorouders om het Earth / UK / Devon-ding te laten zien
  2. Alle kinderen tonen "Bestemmingen in Europa" (de items)
  3. Alle nakomelingen om "Bestemmingen in Europa" te tonen (de tellingen)

Ik zou gaan voor gematerialiseerde paden, aangezien dit soort hiërarchie zelden verandert (alleen in geval van oorlog, opstand enz.).

Maak een varchar-kolom met de naam path , indexeer het en vul het met de waarde als volgt:

1:234:6345:45454:

waarbij de nummers de primaire sleutels zijn van de betreffende ouders, in de juiste volgorde (1 voor Europa, 234 voor VK enz.)

Je hebt ook een tabel nodig met de naam levels om nummers van 1 te behouden tot 20 (of welk maximaal nestniveau je maar wilt).

Om alle voorouders te selecteren:

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

Om alle kinderen te selecteren en plaatsen binnen hen te tellen:

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


  1. Doet MySQL-weergave altijd een volledige tabelscan?

  2. Hoe kan ik een transactie-impasse opheffen?

  3. Waarom en waar INDEX's gebruiken - voor- en nadelen

  4. Moeten de primaire sleutels van de database gehele getallen zijn?