sql >> Base de Datos >  >> RDS >> Mysql

¿Qué modelo jerárquico debo usar? ¿Adyacencia, anidado o enumerado?

Por lo general, hay tres tipos de consultas en las jerarquías que causan problemas:

  1. Devolver todos los ancestros
  2. Devolver todos los descendientes
  3. Regresar a todos los hijos (descendientes inmediatos).

Aquí hay una pequeña tabla que muestra el rendimiento de diferentes métodos en 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

En children , poor/excellent significa que la respuesta depende de si está mezclando el método con la lista de adyacencia, i. mi. almacenar el parentID en cada registro.

Para su tarea, necesita las tres consultas:

  1. Todos los ancestros para mostrar la cosa de la Tierra/Reino Unido/Devon
  2. Todos los niños deben mostrar "Destinos en Europa" (los elementos)
  3. Todos los descendientes para mostrar "Destinos en Europa" (los condes)

Yo optaría por caminos materializados, ya que este tipo de jerarquía rara vez cambia (solo en caso de guerra, revuelta, etc.).

Cree una columna varchar llamada path , indexarlo y llenarlo con el valor como este:

1:234:6345:45454:

donde los números son claves primarias de los padres apropiados, en el orden correcto (1 para Europa, 234 para Reino Unido, etc.)

También necesitará una tabla llamada levels para mantener los números de 1 a 20 (o el nivel máximo de anidamiento que desee).

Para seleccionar todos los antepasados:

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

Para seleccionar todos los niños y la cantidad de lugares dentro de ellos:

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