Mysql
 sql >> Database >  >> RDS >> Mysql

MySQL:unisci più tabelle in un'unica istruzione

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.