Thông thường, có ba loại truy vấn trong phân cấp gây ra sự cố:
- Trả lại tất cả tổ tiên
- Trả lại tất cả các con cháu
- Trả lại tất cả các con (con cháu trực tiếp).
Đây là một bảng nhỏ hiển thị hiệu suất của các phương thức khác nhau trong 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
Trong children
, poor/excellent
có nghĩa là câu trả lời phụ thuộc vào việc bạn có đang trộn phương thức với danh sách kề hay không, i. e. lưu trữ parentID
trong mỗi bản ghi.
Đối với nhiệm vụ của mình, bạn cần cả ba truy vấn:
- Tất cả tổ tiên để chỉ cho Trái đất / Vương quốc Anh / Devon vật
- Tất cả trẻ em để hiển thị "Các điểm đến ở Châu Âu" (các mục)
- Tất cả các phần tử con để hiển thị "Các điểm đến ở Châu Âu" (số lượng)
Tôi sẽ đi theo những con đường cụ thể hóa, vì loại thứ bậc này hiếm khi thay đổi (chỉ trong trường hợp chiến tranh, nổi dậy, v.v.).
Tạo một cột varchar có tên là path
, lập chỉ mục nó và điền vào nó với giá trị như sau:
1:234:6345:45454:
trong đó các số là khóa chính của cha mẹ thích hợp, theo đúng thứ tự (1
đối với Châu Âu, 234
cho Vương quốc Anh, v.v.)
Bạn cũng sẽ cần một bảng có tên là levels
để giữ các số từ 1
thành 20
(hoặc bất kỳ mức lồng ghép tối đa nào bạn muốn).
Để chọn tất cả tổ tiên:
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
Để chọn tất cả trẻ em và số lượng địa điểm trong chúng:
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