Hoe krijg ik alle nakomelingen van een boomknooppunt met recursieve zoekopdracht in MySql?
Het is echt een probleem voor MySql, en het is een belangrijk punt voor deze vraag, maar je hebt nog steeds enkele keuzes.
Ervan uitgaande dat u over dergelijke voorbeeldgegevens beschikt, niet zoveel als uw steekproef, maar voldoende om aan te tonen:
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);
Eerste keuze:niveaucode
Zoals de voorbeeldgegevens van de naamkolom in de treeNode-tabel. (Ik weet niet hoe ik het in het Engels moet zeggen, geef commentaar op de juiste uitdrukking van level code
.)
Om alle nakomelingen van C1
te krijgen of G1
zou zo simpel kunnen zijn:
select * from treeNode where type = 'CATEGORY' and name like 'C1%' ;
select * from treeNode where type = 'GROUP' and name like 'G1%' ;
Ik geef de voorkeur aan deze aanpak, we moeten zelfs deze code genereren voordat treeNode in de toepassing wordt opgeslagen. Het is efficiënter dan een recursieve query of procedure als we een groot aantal records hebben. Ik denk dat dit een goede denormalisatiebenadering is.
Met deze benadering, de verklaring je wilt met join zou kunnen zijn:
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
Lief, nietwaar?
Tweede keuze:niveaunummer
Voeg een niveaukolom toe aan de treeNode-tabel, zoals weergegeven in de DDL.
Niveaunummer is veel gemakkelijker te onderhouden dan niveaucode in toepassing.
Met niveaunummer om alle nakomelingen van C1
te krijgen of G1
heb een klein trucje als dit nodig:
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) ;
Deze benadering is langzamer dan de eerste, maar nog steeds sneller dan een recursieve zoekopdracht.
De volledige sql van de vraag is weggelaten. U hoeft alleen die twee subquery's van C en G te vervangen door twee bovenstaande query's.
Opmerking:
Er zijn veel vergelijkbare benaderingen, zoals hier
, hier
, of zelfs hier
. Ze werken niet tenzij ze zijn besteld op niveaunummer of niveaucode. Je zou de laatste vraag kunnen testen in deze SqlFiddle
door order by level
te wijzigen om order by id
om de verschillen te zien.
Nog een keuze:het geneste setmodel
Raadpleeg deze blog , heb ik nog niet getest. Maar ik denk dat het vergelijkbaar is met de laatste twee keuzes.
Je moet een linkernummer en een rechternummer aan de treenode-tabel toevoegen om alle afstammelingen-ID's tussen hen in te sluiten.