Đối với MySQL 8+: sử dụng with
cú pháp đệ quy .
Đối với MySQL 5.x: sử dụng các biến nội tuyến, ID đường dẫn hoặc tự tham gia.
MySQL 8+
with recursive cte (id, name, parent_id) as (
select id,
name,
parent_id
from products
where parent_id = 19
union all
select p.id,
p.name,
p.parent_id
from products p
inner join cte
on p.parent_id = cte.id
)
select * from cte;
Giá trị được chỉ định trong parent_id = 19
phải được đặt thành giá trị id
gốc mà bạn muốn chọn tất cả các giá trị con của.
MySQL 5.x
Đối với các phiên bản MySQL không hỗ trợ Biểu thức Bảng Chung (lên đến phiên bản 5.7), bạn sẽ đạt được điều này với truy vấn sau:
select id,
name,
parent_id
from (select * from products
order by parent_id, id) products_sorted,
(select @pv := '19') initialisation
where find_in_set(parent_id, @pv)
and length(@pv := concat(@pv, ',', id))
Đây là một trò đùa .
Ở đây, giá trị được chỉ định trong @pv := '19'
phải được đặt thành giá trị id
gốc mà bạn muốn chọn tất cả các con của.
Điều này cũng sẽ hoạt động nếu cha mẹ có nhiều con. Tuy nhiên, yêu cầu mỗi bản ghi phải đáp ứng điều kiện parent_id < id
, nếu không kết quả sẽ không đầy đủ.
Các phép gán biến bên trong một truy vấn
Truy vấn này sử dụng cú pháp MySQL cụ thể: các biến được gán và sửa đổi trong quá trình thực thi của nó. Một số giả định được đưa ra về thứ tự thực hiện:
- Các
from
khoản nào được tính trước. Vì vậy, đó là nơi @pv
được khởi tạo.
- Các
where
khoản được đánh giá cho mỗi bản ghi theo thứ tự thu hồi từ các from
bí danh. Vì vậy, đây là nơi đặt một điều kiện để chỉ bao gồm các bản ghi mà cây mẹ đã được xác định là ở trong cây con (tất cả con cháu của cây mẹ chính được thêm dần vào @pv
).
- Các điều kiện trong điều
where
khoản này được đánh giá theo thứ tự và việc đánh giá bị gián đoạn khi kết quả tổng thể đã chắc chắn. Do đó, điều kiện thứ hai phải ở vị trí thứ hai, vì nó thêm id
vào danh sách cha và điều này chỉ xảy ra nếu id
điều kiện đầu tiên vượt qua. Các length
chức năng được chỉ gọi là để đảm bảo điều kiện này luôn là sự thật, ngay cả khi pv
chuỗi sẽ vì một lý do mang lại một giá trị falsy.
Nhìn chung, người ta có thể thấy những giả định này quá rủi ro để dựa vào. Các tài liệu cảnh báo:
bạn có thể nhận được kết quả mà bạn mong đợi, nhưng điều này không được đảm bảo [...] thứ tự đánh giá cho các biểu thức liên quan đến biến người dùng là không xác định.
Vì vậy, mặc dù nó hoạt động nhất quán với truy vấn ở trên, thứ tự đánh giá vẫn có thể thay đổi, chẳng hạn như khi bạn thêm điều kiện hoặc sử dụng truy vấn này như một dạng xem hoặc truy vấn phụ trong một truy vấn lớn hơn. Đây là một "tính năng" sẽ bị loại bỏ trong bản phát hành MySQL trong tương lai :
Các bản phát hành trước của MySQL cho phép gán giá trị cho một biến người dùng trong các câu lệnh khác SET
. Chức năng này được hỗ trợ trong MySQL 8.0 để tương thích ngược nhưng có thể bị loại bỏ trong bản phát hành MySQL trong tương lai.
Như đã nêu ở trên, từ MySQL 8.0 trở đi, bạn nên sử dụng with
cú pháp đệ quy .
Hiệu quả
Đối với các tập dữ liệu rất lớn, giải pháp này có thể bị chậm, vì find_in_set
thao tác này không phải là cách lý tưởng nhất để tìm một số trong danh sách, chắc chắn không phải trong danh sách đạt kích thước theo cùng thứ tự độ lớn với số bản ghi được trả về.
Phương án 1: with recursive
,connect by
Ngày càng nhiều cơ sở dữ liệu triển khai cú pháp tiêu chuẩn ISO SQL: 1999WITH [RECURSIVE]
cho các truy vấn đệ quy (ví dụ: Postgres 8.4+ , SQL Server 2005+ , DB2 , Oracle 11gR2 + , SQLite 3.8.4+ , Firebird 2.1+ , H2 , HyperSQL 2.1.0+ , Teradata , MariaDB 10.2.2+ ). Và kể từ phiên bản 8.0, MySQL cũng hỗ trợ nó . Xem phần trên cùng của câu trả lời này để biết cú pháp sử dụng.
Một số cơ sở dữ liệu có cú pháp thay thế, không chuẩn để tra cứu thứ bậc, chẳng hạn như CONNECT BY
mệnh đề có sẵn trên Oracle , DB2 , Informix , CUBRID và các cơ sở dữ liệu khác.
MySQL phiên bản 5.7 không cung cấp một tính năng như vậy. Khi công cụ cơ sở dữ liệu của bạn cung cấp cú pháp này hoặc bạn có thể chuyển sang một cú pháp có, thì đó chắc chắn là lựa chọn tốt nhất để sử dụng. Nếu không, sau đó cũng xem xét các lựa chọn thay thế sau đây.
Thay thế 2: Số nhận dạng kiểu đường dẫn
Mọi thứ trở nên dễ dàng hơn rất nhiều nếu bạn gán id
các giá trị chứa thông tin phân cấp: một đường dẫn. Ví dụ: trong trường hợp của bạn, nó có thể trông như thế này:
TÔI |
TÊN |
19 |
Loại 1 |
19/1 |
danh mục2 |
19/1/1 |
danh mục3 |
19/1/1/1 |
danh mục 4 |
Sau đó, của bạn select
sẽ giống như thế này:
select id,
name
from products
where id like '19/%'
Phương án 3: Tự tham gia lặp lại
Nếu bạn biết giới hạn trên cho mức độ sâu của cây phân cấp, bạn có thể sử dụng sql
truy vấn tiêu chuẩn như sau:
select p6.parent_id as parent6_id,
p5.parent_id as parent5_id,
p4.parent_id as parent4_id,
p3.parent_id as parent3_id,
p2.parent_id as parent2_id,
p1.parent_id as parent_id,
p1.id as product_id,
p1.name
from products p1
left join products p2 on p2.id = p1.parent_id
left join products p3 on p3.id = p2.parent_id
left join products p4 on p4.id = p3.parent_id
left join products p5 on p5.id = p4.parent_id
left join products p6 on p6.id = p5.parent_id
where 19 in (p1.parent_id,
p2.parent_id,
p3.parent_id,
p4.parent_id,
p5.parent_id,
p6.parent_id)
order by 1, 2, 3, 4, 5, 6, 7;
Xem trò chơi này
Điều where
kiện chỉ định cha mẹ nào bạn muốn truy xuất con cháu của. Bạn có thể mở rộng truy vấn này với nhiều cấp hơn nếu cần.