Mysql
 sql >> Base de données >  >> RDS >> Mysql

MySQL :joindre plusieurs tables sur une seule déclaration

Comment obtenir tous les descendants d'un nœud d'arbre avec une requête récursive dans MySql ?

C'est vraiment un problème pour MySql, et c'est un point clé de cette question, mais vous avez encore quelques choix.

En supposant que vous disposiez de tels exemples de données, pas autant que votre échantillon, mais suffisamment pour démontrer :

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);

Premier choix :code de niveau

Comme les exemples de données de la colonne de nom dans la table treeNode. (Je ne sais pas comment le dire en anglais, commentez-moi sur l'expression correcte de level code .)

Pour obtenir tous les descendants de C1 ou G1 pourrait être simple comme ceci :

select * from treeNode where type = 'CATEGORY' and name like 'C1%' ;
select * from treeNode where type = 'GROUP' and name like 'G1%' ;

Je préfère beaucoup cette approche, j'ai même besoin que nous générions ces codes avant que treeNode ne soit enregistré dans l'application. Ce sera plus efficace qu'une requête ou une procédure récursive lorsque nous avons un grand nombre d'enregistrements. Je pense que c'est une bonne approche de dénormalisation.

Avec cette approche, la déclaration vous voulez avec jointure pourrait être :

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

Mignon, n'est-ce pas ?

Deuxième choix :numéro de niveau

Ajoutez une colonne de niveau à la table treeNode, comme indiqué dans le DDL.

Le numéro de niveau est beaucoup plus facile à gérer que le code de niveau en application.

Avec numéro de niveau pour obtenir tous les descendants de C1 ou G1 besoin d'une petite astuce comme celle-ci :

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) ;

Cette approche est plus lente que la première, mais toujours plus rapide que la requête récursive.

Le sql complet à la question omis. Il suffit de remplacer ces deux sous-requêtes de C et G par les deux requêtes ci-dessus.

Remarque :

Il existe de nombreuses approches similaires telles que ici , ici , ou même ici . Ils ne fonctionneront que s'ils sont classés par numéro de niveau ou code de niveau. Vous pouvez tester la dernière requête dans ce SqlFiddle en changeant order by level pour order by id pour voir les différences.

Un autre choix :le modèle d'ensemble imbriqué

Veuillez vous référer à ce blog , je n'ai pas encore testé. Mais je pense que c'est similaire aux deux derniers choix.

Il faut que vous ajoutiez un nombre gauche et un nombre droit à la table treenode pour enfermer tous les identifiants des descendants entre eux.