Généralement, il existe trois types de requêtes dans les hiérarchies qui causent des problèmes :
- Renvoyer tous les ancêtres
- Renvoyer tous les descendants
- Renvoyer tous les enfants (descendants immédiats).
Voici un petit tableau qui montre les performances des différentes méthodes dans 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
Dans children
, poor/excellent
signifie que la réponse dépend si vous mélangez la méthode avec la liste de contiguïté, c'est-à-dire. e. stocker le parentID
dans chaque enregistrement.
Pour votre tâche, vous avez besoin des trois requêtes :
- Tous les ancêtres pour montrer le truc Terre / Royaume-Uni / Devon
- Tous les enfants doivent montrer "Destinations en Europe" (les articles)
- Tous les descendants doivent afficher "Destinations en Europe" (les chiffres)
J'opterais pour des voies matérialisées, car ce type de hiérarchie change rarement (uniquement en cas de guerre, de révolte, etc.).
Créez une colonne varchar appelée path
, indexez-le et remplissez-le avec la valeur comme ceci :
1:234:6345:45454:
où les nombres sont les clés primaires des parents appropriés, dans le bon ordre (1
pour l'Europe, 234
pour le Royaume-Uni, etc.)
Vous aurez également besoin d'un tableau appelé levels
pour conserver les nombres à partir de 1
à 20
(ou le niveau d'imbrication maximal que vous souhaitez).
Pour sélectionner tous les ancêtres :
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
Pour sélectionner tous les enfants et le nombre de lieux qu'ils contiennent :
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