Đối với MySQL 8+: sử dụng đệ quy with
cú pháp.
Đố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
nên được đặt thành id
của cha mẹ mà bạn muốn chọn tất cả các 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à fiddle .
Đây, giá trị được chỉ định trong @pv := '19'
nên được đặt thành id
của cha mẹ 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 bọn trẻ. Tuy nhiên, 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:
-
from
mệnh đề được đánh giá đầu tiên. Vì vậy, đó là nơi@pv
được khởi tạo. -
where
mệnh đề được đánh giá cho mỗi bản ghi theo thứ tự truy xuất từ 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ả các con của cây mẹ chính được thêm dần vào@pv
). - Các điều kiện trong
where
này mệnh đề được đánh giá theo thứ tự và quá trình đánh giá bị gián đoạn khi kết quả tổng thể là chắc chắn. Do đó, điều kiện thứ hai phải ở vị trí thứ hai, vì nó thêmid
vào danh sách mẹ và điều này chỉ xảy ra nếuid
vượt qua điều kiện đầu tiên.length
hàm chỉ được gọi để đảm bảo điều kiện này luôn đúng, ngay cả khipv
vì lý do nào đó chuỗi sẽ mang lại giá trị giả.
Nhìn chung, người ta có thể thấy những giả định này quá rủi ro để dựa vào. tài liệu cảnh báo:
bạn có thể nhận được kết quả như 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 làm 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" mà sẽ bị xóa trong tương lai Bản phát hành MySQL :
Các bản phát hành trước của MySQL giúp có thể gán giá trị cho biến người dùng trong các câu lệnh khác với
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ị xóa 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.
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ể chạy chậm, vì find_in_set
hoạt động 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ố lượng bản ghi được trả về.
Phương án 1:with
đệ quy , connect by
Ngày càng nhiều cơ sở dữ liệu triển khai tiêu chuẩn ISO SQL:1999 WITH [RECURSIVE]
cú pháp
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 a> , 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, hãy xem xét các lựa chọn thay thế sau.
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 chỉ định 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:
ID | TÊN |
---|---|
19 | category1 |
19/1 | category2 |
19/1/1 | category3 |
19/1/1/1 | category4 |
Sau đó, select
của bạn 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 về độ sâu của cây phân cấp, bạn có thể sử dụng sql
tiêu chuẩn truy vấ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 fiddle này
where
điều 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.