Làm cách nào để lấy tất cả con cháu từ một nút cây bằng truy vấn đệ quy trong MySql?
Đó thực sự là một vấn đề đối với MySql và đó là điểm mấu chốt của câu hỏi này, nhưng bạn vẫn có một số lựa chọn.
Giả sử bạn có dữ liệu mẫu như vậy, không nhiều bằng mẫu của bạn nhưng đủ để chứng minh:
create table treeNode(
id int, parent_id int, name varchar(10), type varchar(10),level int);
insert into treeNode
(id, parent_id, name, type, level) values
( 1, 0, 'C1 ', 'CATEGORY', 1),
( 2, 1, 'C1.1 ', 'CATEGORY', 2),
( 3, 2, 'C1.1.1', 'CATEGORY', 3),
( 4, 1, 'C1.2 ', 'CATEGORY', 2),
( 5, 4, 'C1.2.1', 'CATEGORY', 3),
( 3, 8, 'G1.1.1', 'GROUP', 3),
( 4, 9, 'G1.2 ', 'GROUP', 2),
( 5, 4, 'G1.2.1', 'GROUP', 3),
( 8, 9, 'G1.1 ', 'GROUP', 2),
( 9, 0, 'G1 ', 'GROUP', 1);
Lựa chọn đầu tiên: mã cấp
Giống như dữ liệu mẫu của cột tên trong bảng treeNode. ( Tôi không biết phải nói thế nào bằng tiếng Anh, hãy bình luận cho tôi về cách diễn đạt đúng của level code
.)
Để nhận tất cả các con của C1
hoặc G1
có thể đơn giản như thế này:
select * from treeNode where type = 'CATEGORY' and name like 'C1%' ;
select * from treeNode where type = 'GROUP' and name like 'G1%' ;
Tôi rất thích cách tiếp cận này, thậm chí cần chúng tôi tạo những đoạn mã này trước khi treeNode được lưu trong ứng dụng. Nó sẽ hiệu quả hơn truy vấn hoặc thủ tục đệ quy khi chúng ta có số lượng lớn các bản ghi. Tôi nghĩ đây là một cách tiếp cận không chuẩn hóa tốt.
Với cách tiếp cận này, tuyên bố bạn muốn với tham gia có thể là:
SELECT distinct p.* --if there is only one tree node for a product, distinct is not needed
FROM product p
JOIN product_type pt
ON pt.id= p.parent_id -- to get product type of a product
JOIN linked_TreeNode LC
ON LC.product_id= p.id -- to get tree_nodes related to a product
JOIN (select * from treeNode where type = 'CATEGORY' and name like 'C1%' ) C --may replace C1% to concat('$selected_cat_name','%')
ON LC.treeNode_id = C.id
JOIN (select * from treeNode where type = 'GROUP' and name like 'G1%' ) G --may replace G1% to concat('$selected_group_name','%')
ON LC.treeNode_id = G.id
WHERE pt.name = '$selected_type' -- filter selected product type, assuming using product.name, if using product.parent_id, can save one join by pt like your original sql
Ngọt ngào phải không?
Lựa chọn thứ hai:số cấp
Nối cột mức vào bảng treeNode, như được hiển thị trong DDL.
Số cấp dễ duy trì hơn nhiều so với mã cấp trong ứng dụng.
Với số cấp để nhận tất cả các con của C1
hoặc G1
cần một mẹo nhỏ như sau:
SELECT id, parent_id, name, type, @pv:=concat(@pv,',',id) as link_ids
FROM (select * from treeNode where type = 'CATEGORY' order by level) as t
JOIN (select @pv:='1')tmp
WHERE find_in_set(parent_id,@pv)
OR find_in_set(id,@pv);
-- get all descendants of `C1`
SELECT id, parent_id, name, type, @pv:=concat(@pv,',',id) as link_ids
FROM (select * from treeNode where type = 'GROUP' order by level) as t
JOIN (select @pv:=',9,')tmp
WHERE find_in_set(parent_id,@pv)
OR find_in_set(id,@pv) ;
Cách tiếp cận này chậm hơn cách tiếp cận đầu tiên, nhưng vẫn nhanh hơn truy vấn đệ quy.
Đã bỏ qua sql đầy đủ cho câu hỏi. Chỉ cần thay thế hai truy vấn con C và G bằng hai truy vấn trên.
Lưu ý:
Có nhiều cách tiếp cận tương tự như tại đây
, tại đây
hoặc thậm chí tại đây
. Chúng sẽ không hoạt động trừ khi được sắp xếp theo số cấp hoặc mã cấp. Bạn có thể kiểm tra truy vấn cuối cùng trong SqlFiddle
này bằng cách thay đổi order by level
đến order by id
để thấy sự khác biệt.
Lựa chọn khác:Mô hình tập hợp lồng nhau
Vui lòng tham khảo blog này , Tôi đã không thử nghiệm được nêu ra. Nhưng tôi nghĩ nó tương tự như hai lựa chọn cuối cùng.
Bạn cần thêm một số bên trái và một số bên phải vào bảng treenode để bao gồm tất cả các id của con cháu giữa chúng.