Bagaimana cara mendapatkan semua turunan dari simpul pohon dengan kueri rekursif di MySql?
Ini benar-benar masalah bagi MySql, dan ini adalah poin kunci untuk pertanyaan ini, tetapi Anda masih memiliki beberapa pilihan.
Dengan asumsi Anda memiliki data sampel seperti itu, tidak sebanyak sampel Anda tetapi cukup untuk menunjukkan:
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);
Pilihan pertama:kode level
Seperti contoh data kolom nama pada tabel treeNode. (Saya tidak tahu bagaimana mengatakannya dalam bahasa Inggris, beri komentar saya tentang ungkapan yang benar dari level code
.)
Untuk mendapatkan semua turunan C1
atau G1
bisa jadi sederhana seperti ini:
select * from treeNode where type = 'CATEGORY' and name like 'C1%' ;
select * from treeNode where type = 'GROUP' and name like 'G1%' ;
Saya sangat menyukai pendekatan ini, bahkan membutuhkan kami untuk membuat kode ini sebelum treeNode disimpan di application. Ini akan lebih efisien daripada kueri atau prosedur rekursif ketika kita memiliki sejumlah besar catatan. Saya pikir ini adalah pendekatan denormalisasi yang baik.
Dengan pendekatan ini, pernyataan Anda ingin dengan bergabung bisa jadi:
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
Manis, bukan?
Pilihan kedua:nomor level
Tambahkan kolom level ke tabel treeNode, seperti yang ditunjukkan di DDL.
Nomor level jauh lebih mudah dipertahankan daripada kode level dalam aplikasi.
Dengan nomor level untuk mendapatkan semua keturunan C1
atau G1
butuh sedikit trik seperti ini :
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) ;
Pendekatan ini lebih lambat dari yang pertama, tetapi masih lebih cepat daripada kueri rekursif.
sql lengkap untuk pertanyaan dihilangkan. Hanya perlu mengganti dua subquery C dan G dengan dua query di atas.
Catatan:
Ada banyak pendekatan serupa seperti di sini
, di sini
, atau bahkan di sini
. Mereka tidak akan bekerja kecuali diperintahkan oleh nomor level atau kode level. Anda dapat menguji kueri terakhir di SqlFiddle
ini dengan mengubah order by level
untuk order by id
untuk melihat perbedaannya.
Pilihan Lain:Model Kumpulan Bersarang
Silakan merujuk ke blog ini , saya belum tes. Tapi saya pikir ini mirip dengan dua pilihan terakhir.
Anda perlu menambahkan nomor kiri dan nomor kanan ke tabel treenode untuk menyertakan semua id keturunan di antara mereka.