Jak získat všechny potomky z uzlu stromu pomocí rekurzivního dotazu v MySql?
Je to opravdu problém pro MySql a je to klíčový bod této otázky, ale stále máte několik možností.
Předpokládejme, že máte taková ukázková data, ne tolik jako váš vzorek, ale dost na to, abyste je mohli demonstrovat:
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);
První volba:kód úrovně
Stejně jako ukázková data sloupce názvu v tabulce treeNode. (Nevím, jak se to řekne anglicky, okomentujte mě ohledně správného výrazu level code
.)
Chcete-li získat všechny potomky C1
nebo G1
může to být jednoduché takto:
select * from treeNode where type = 'CATEGORY' and name like 'C1%' ;
select * from treeNode where type = 'GROUP' and name like 'G1%' ;
Tento přístup velmi preferuji, dokonce potřebuji, abychom tento kód vygenerovali před uložením treeNode do aplikace. Bude to efektivnější než rekurzivní dotaz nebo procedura, když máme velké množství záznamů. Myslím, že je to dobrý denormalizační přístup.
S tímto přístupem výrok chcete se připojit může být:
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
Milé, že?
Druhá volba:číslo úrovně
Připojte sloupec úrovně k tabulce treeNode, jak je znázorněno v DDL.
Údržba čísla úrovně je mnohem jednodušší než kód úrovně v aplikaci.
S číslem úrovně pro získání všech potomků C1
nebo G1
potřebujete malý trik, jako je tento:
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) ;
Tento přístup je pomalejší než první, ale stále rychlejší než rekurzivní dotaz.
Úplné sql na otázku bylo vynecháno. Stačí pouze nahradit tyto dva dílčí dotazy C a G dvěma výše uvedenými dotazy.
Poznámka:
Existuje mnoho podobných přístupů, jako je zde
, zde
nebo dokonce zde
. Nebudou fungovat, pokud nebudou objednány podle čísla úrovně nebo kódu úrovně. Poslední dotaz můžete otestovat v tomto SqlFiddle
změnou order by level
order by id
abyste viděli rozdíly.
Další možnost:Model vnořené sady
Podívejte se prosím na tento blog , ještě jsem netestoval. Ale myslím, že je to podobné jako u posledních dvou možností.
Je potřeba, abyste do tabulky uzlů stromu přidali levé a pravé číslo, abyste mezi ně uzavřeli ID všech potomků.