Đối với MySQL 8+: sử dụng with
cú pháp đệ quy .
Đối với MySQL 5.x: sử dụng 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ànhid
cha mẹ bạn muốn chọn tất cả các hậu duệ 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 (tối đa 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 câu đố .
Ở đây, giá trị được chỉ định trong @pv := '19'
phải được đặt thànhid
cha mẹ bạn muốn chọn tất cả các hậu duệ của.
Điều này cũng sẽ làm việc 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 được hoàn thành.
Bài tập biế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. Một số giả định được đưa ra về thứ tự thực hiện:
- Điều
from
khoản được đánh giá đầu tiên. 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 một điều kiện được đưa vào chỉ bao gồm các bản ghi mà cha mẹ đã được xác định là đang ở trong cây con cháu (tất cả các hậu duệ của cha mẹ chính được thêm dần vào @pv
).
- Các điều kiện trong
where
mệnh đề này được đánh giá theo thứ tự và việc đánh giá bị gián đoạn một khi tổng kết quả là chắc chắn. Do đó, điều kiện thứ hai phải ở vị trí thứ hai, vì nó thêm id
danh sách cha mẹ và điều này chỉ xảy ra nếu id
vượt qua điều kiện đầu tiên. 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 các biến người dùng không được 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, ví dụ khi bạn thêm điều kiện hoặc sử dụng truy vấn này làm chế độ xem hoặc truy vấn phụ trong truy vấn lớn hơn. Đây là một "tính năng" sẽ bị xóa trong bản phát hành MySQL trong tương lai :
Các bản phát hành trước đây của MySQL cho phép gán giá trị cho 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ói ở trên, từ MySQL 8.0 trở đi, bạn nên sử dụng đệ quy with
cú pháp .
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 không phải là cách lý tưởng nhất để tìm số trong danh sách, chắc chắn không phải trong danh sách đạt kích thước theo thứ tự độ lớn như số lượng bản ghi được trả về.
Phương án 1 : with recursive
,connect by
Ngày càng có 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 , HyperQuery 2.1.0+ , Teradata , MariaDB 10.2.2+ ). Và kể từ phiên bản 8.0, MySQL cũng hỗ trợ nó . Xem đầu câu trả lời này để biết cú pháp sử dụng.
Một số cơ sở dữ liệu có một cú pháp thay thế, không chuẩn cho các tra cứu phân cấp, 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.
Phiên bản MySQL 5.7 không cung cấp 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ể di chuyển sang một cú pháp đó, thì đó chắc chắn là lựa chọn tốt nhất để thực hiện. Nếu không, sau đó cũng xem xét các lựa chọn thay thế sau đây.
Phương án 2: Mã định danh kiểu đường dẫn
Mọi thứ trở nên dễ dàng hơn rất nhiều nếu bạn chỉ định id
các giá trị có 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:
ID | NAME
19 | category1
19/1 | category2
19/1/1 | category3
19/1/1/1 | category4
Sau đó, bạn select
sẽ trông như thế này:
select id,
name
from products
where id like '19/%'
Phương án 3: Tự lặp đi lặp lại
Nếu bạn biết giới hạn trên cho việc cây phân cấp của bạn có thể trở nên sâu đến mức nào, bạn có thể sử dụng sql
truy vấn tiêu chuẩn như thế này:
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 câu đố này
Điều where
kiện chỉ định cha mẹ mà bạn muốn lấy 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.