Обикновено има три вида заявки в йерархиите, които причиняват проблеми:
- Върнете всички предци
- Връщане на всички потомци
- Върнете всички деца (непосредствени потомци).
Ето малка таблица, която показва производителността на различни методи в 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
В children
, poor/excellent
означава, че отговорът зависи от това дали смесвате метода със списък на съседство, т.е. д. съхраняване на parentID
във всеки запис.
За вашата задача са ви необходими и трите заявки:
- Всички предци, за да се покаже нещото Земята/Обединеното кралство/Девън
- Всички деца за показване на „Дестинации в Европа“ (артикулите)
- Всички потомци за показване на „Дестинации в Европа“ (броя)
Бих тръгнал по материализирани пътища, тъй като този вид йерархия рядко се променя (само в случай на война, бунт и т.н.).
Създайте колона varchar, наречена path
, индексирайте го и го попълнете със стойността по следния начин:
1:234:6345:45454:
където числата са първични ключове на съответните родители, в правилен ред (1
за Европа, 234
за Обединеното кралство и др.)
Ще ви трябва и таблица, наречена levels
за да запазите числата от 1
до 20
(или каквото желаете максимално ниво на влагане).
За да изберете всички предшественици:
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
За да изберете всички деца и броя на местата в тях:
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