Come ottenere tutti i discendenti da un nodo ad albero con query ricorsive in MySql?
È davvero un problema per MySql ed è un punto chiave per questa domanda, ma hai ancora delle scelte.
Supponendo che tu abbia tali dati di esempio, non tanto quanto il tuo campione ma abbastanza per dimostrare:
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);
Prima scelta:codice livello
Come i dati di esempio della colonna name nella tabella treeNode. (Non so come dirlo in inglese, commentami sulla corretta espressione di level code
.)
Per ottenere tutti i discendenti di C1
o G1
potrebbe essere semplice come questo:
select * from treeNode where type = 'CATEGORY' and name like 'C1%' ;
select * from treeNode where type = 'GROUP' and name like 'G1%' ;
Preferisco molto questo approccio, ho anche bisogno che generiamo questo codice prima che treeNode venga salvato nell'applicazione. Sarà più efficiente di una query o procedura ricorsiva quando abbiamo un numero elevato di record. Penso che questo sia un buon approccio di denormalizzazione.
Con questo approccio, la dichiarazione vuoi con unisciti potrebbe essere:
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
Dolce, vero?
Seconda scelta:numero di livello
Aggiungi una colonna di livello alla tabella treeNode, come mostrato nel DDL.
Il numero di livello è molto più facile da mantenere rispetto al codice di livello in applicazione.
Con numero di livello per ottenere tutti i discendenti di C1
o G1
ho bisogno di un piccolo trucco come questo:
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) ;
Questo approccio è più lento del primo, ma comunque più veloce della query ricorsiva.
L'intero sql alla domanda omesso. Devi solo sostituire quelle due sottoquery di C e G con le due query precedenti.
Nota:
Esistono molti approcci simili come qui
, qui
o anche qui
. Non funzioneranno a meno che non vengano ordinati per numero di livello o codice di livello. Puoi testare l'ultima query in questo SqlFiddle
modificando order by level
a order by id
per vedere le differenze.
Un'altra scelta:il modello del set annidato
Fare riferimento a questo blog , non ho ancora testato. Ma penso che sia simile alle ultime due scelte.
È necessario aggiungere un numero sinistro e un numero destro alla tabella dei nodi albero per racchiudere tra loro gli ID di tutti i discendenti.