Zazwyczaj w hierarchii występują trzy rodzaje zapytań, które powodują problemy:
- Zwróć wszystkich przodków
- Zwróć wszystkich potomków
- Zwróć wszystkie dzieci (bezpośrednich potomków).
Oto mała tabela, która pokazuje wydajność różnych metod w 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
W children
, poor/excellent
oznacza, że odpowiedź zależy od tego, czy mieszasz metodę z listą sąsiedztwa, tj. mi. przechowywanie parentID
w każdym rekordzie.
Do swojego zadania potrzebujesz wszystkich trzech zapytań:
- Wszyscy przodkowie, aby pokazać Ziemię/Wielką Brytanię/Devon
- Wszystkie dzieci mają pokazać „Miejsca docelowe w Europie” (przedmioty)
- Wszyscy potomkowie mają pokazać „Miejsca docelowe w Europie” (liczba)
Wybrałbym zmaterializowane ścieżki, ponieważ tego rodzaju hierarchia rzadko się zmienia (tylko w przypadku wojny, buntu itp.).
Utwórz kolumnę varchar o nazwie path
, zindeksuj go i wypełnij wartością w ten sposób:
1:234:6345:45454:
gdzie liczby są kluczami podstawowymi odpowiednich rodziców, we właściwej kolejności (1
dla Europy, 234
dla Wielkiej Brytanii itp.)
Będziesz także potrzebować tabeli o nazwie levels
aby zachować liczby od 1
do 20
(lub jakikolwiek inny maksymalny poziom zagnieżdżenia, jaki chcesz).
Aby wybrać wszystkich przodków:
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
Aby wybrać wszystkie dzieci i liczbę znajdujących się w nich miejsc:
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