Mysql
 sql >> Database >  >> RDS >> Mysql

Quale modello gerarchico dovrei usare? Adiacenza, nidificato o enumerato?

In genere, ci sono tre tipi di query nelle gerarchie che causano problemi:

  1. Restituisci tutti gli antenati
  2. Restituisci tutti i discendenti
  3. Restituisci tutti i figli (discendenti immediati).

Ecco una piccola tabella che mostra le prestazioni di diversi metodi in MySQL :

                        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 significa che la risposta dipende dal fatto che stai mescolando il metodo con l'elenco di adiacenza, i. e. memorizzazione del parentID in ogni record.

Per il tuo compito, hai bisogno di tutte e tre le query:

  1. Tutti gli antenati mostreranno la cosa Terra/Regno Unito/Devon
  2. Tutti i bambini mostreranno "Destinazioni in Europa" (gli oggetti)
  3. Tutti i discendenti mostreranno "Destinazioni in Europa" (i conti)

Andrei per strade materializzate, poiché questo tipo di gerarchia cambia raramente (solo in caso di guerra, rivolta, ecc.).

Crea una colonna varchar chiamata path , indicizzalo e riempilo con il valore in questo modo:

1:234:6345:45454:

dove i numeri sono chiavi primarie dei genitori appropriati, nell'ordine corretto (1 per l'Europa, 234 per il Regno Unito ecc.)

Avrai anche bisogno di una tabella chiamata levels per mantenere i numeri da 1 a 20 (o qualsiasi livello di nidificazione massimo che desideri).

Per selezionare tutti gli antenati:

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

Per selezionare tutti i bambini e il conteggio dei posti al loro interno:

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