Pour MySQL 8+ : utilisez le récursif with
syntaxe.
Pour MySQL 5.x : utilisez des variables en ligne, des ID de chemin ou des auto-jointures.
MySQL 8+
with recursive cte (id, name, parent_id) as (
select id,
name,
parent_id
from products
where parent_id = 19
union all
select p.id,
p.name,
p.parent_id
from products p
inner join cte
on p.parent_id = cte.id
)
select * from cte;
La valeur spécifiée dans parent_id = 19
doit être défini sur id
du parent dont vous souhaitez sélectionner tous les descendants.
MySQL 5.x
Pour les versions de MySQL qui ne prennent pas en charge les expressions de table communes (jusqu'à la version 5.7), vous y parviendrez avec la requête suivante :
select id,
name,
parent_id
from (select * from products
order by parent_id, id) products_sorted,
(select @pv := '19') initialisation
where find_in_set(parent_id, @pv)
and length(@pv := concat(@pv, ',', id))
Voici un violon .
Ici, la valeur spécifiée dans @pv := '19'
doit être défini sur id
du parent dont vous souhaitez sélectionner tous les descendants.
Cela fonctionnera également si un parent a plusieurs enfants. Cependant, il est nécessaire que chaque enregistrement remplisse la condition parent_id < id
, sinon les résultats ne seront pas complets.
Affectations de variables dans une requête
Cette requête utilise une syntaxe MySQL spécifique :des variables sont affectées et modifiées lors de son exécution. Certaines hypothèses sont faites sur l'ordre d'exécution :
- Le
from
clause est évaluée en premier. C'est donc là que@pv
est initialisé. - Le
where
la clause est évaluée pour chaque enregistrement dans l'ordre d'extraction depuis lefrom
pseudonymes. C'est donc ici qu'une condition est posée pour n'inclure que les enregistrements dont le parent a déjà été identifié comme étant dans l'arbre descendant (tous les descendants du parent primaire sont progressivement ajoutés à@pv
). - Les conditions dans ce
where
clause sont évaluées dans l'ordre, et l'évaluation est interrompue une fois que le résultat total est certain. Par conséquent, la deuxième condition doit être à la deuxième place, car elle ajoute leid
à la liste des parents, et cela ne devrait se produire que si leid
passe la première condition. Lalength
la fonction est uniquement appelée pour s'assurer que cette condition est toujours vraie, même si lepv
chaîne donnerait pour une raison quelconque une valeur fausse.
Dans l'ensemble, on peut trouver ces hypothèses trop risquées pour s'y fier. La documentation avertit :
vous pourriez obtenir les résultats que vous attendez, mais cela n'est pas garanti [...] l'ordre d'évaluation des expressions impliquant des variables utilisateur n'est pas défini.
Ainsi, même si cela fonctionne de manière cohérente avec la requête ci-dessus, l'ordre d'évaluation peut toujours changer, par exemple lorsque vous ajoutez des conditions ou utilisez cette requête comme vue ou sous-requête dans une requête plus large. C'est une "fonctionnalité" qui sera supprimée à l'avenir Version MySQL :
Les versions précédentes de MySQL permettaient d'attribuer une valeur à une variable utilisateur dans des instructions autres que
SET
. Cette fonctionnalité est prise en charge dans MySQL 8.0 pour la rétrocompatibilité, mais est susceptible d'être supprimée dans une future version de MySQL.
Comme indiqué ci-dessus, à partir de MySQL 8.0, vous devez utiliser le with
récursif syntaxe.
Efficacité
Pour les très grands ensembles de données, cette solution peut devenir lente, car le find_in_set
n'est pas le moyen le plus idéal pour trouver un nombre dans une liste, certainement pas dans une liste qui atteint une taille du même ordre de grandeur que le nombre d'enregistrements renvoyés.
Alternative 1 :with recursive
, connect by
De plus en plus de bases de données implémentent la SQL:1999 ISO standard WITH [RECURSIVE]
syntaxe
pour les requêtes récursives (par exemple Postgres 8.4+
, SQL Server 2005+
, DB2
, Oracle 11gR2+
, SQLite 3.8.4+
, Firebird 2.1+
, H2
, HyperSQL 2.1.0+
, Teradata , MariaDB 10.2.2+
). Et depuis version 8.0, MySQL le prend également en charge
. Voir le début de cette réponse pour la syntaxe à utiliser.
Certaines bases de données ont une syntaxe alternative non standard pour les recherches hiérarchiques, comme le CONNECT BY
clause disponible sur Oracle
, DB2
, Informix , CUBRID
et d'autres bases de données.
MySQL version 5.7 n'offre pas une telle fonctionnalité. Lorsque votre moteur de base de données fournit cette syntaxe ou que vous pouvez migrer vers celui qui le fait, c'est certainement la meilleure option à choisir. Si ce n'est pas le cas, envisagez également les alternatives suivantes.
Alternative 2 :Identifiants de style chemin
Les choses deviennent beaucoup plus faciles si vous attribuez id
valeurs qui contiennent les informations hiérarchiques :un chemin. Par exemple, dans votre cas, cela pourrait ressembler à ceci :
identifiant | NOM |
---|---|
19 | category1 |
19/1 | category2 |
19/1/1 | category3 |
19/1/1/1 | category4 |
Ensuite, votre select
ressemblerait à ceci :
select id,
name
from products
where id like '19/%'
Alternative 3 :Jointures répétitives
Si vous connaissez une limite supérieure pour la profondeur de votre arborescence hiérarchique, vous pouvez utiliser un sql
standard requête comme celle-ci :
select p6.parent_id as parent6_id,
p5.parent_id as parent5_id,
p4.parent_id as parent4_id,
p3.parent_id as parent3_id,
p2.parent_id as parent2_id,
p1.parent_id as parent_id,
p1.id as product_id,
p1.name
from products p1
left join products p2 on p2.id = p1.parent_id
left join products p3 on p3.id = p2.parent_id
left join products p4 on p4.id = p3.parent_id
left join products p5 on p5.id = p4.parent_id
left join products p6 on p6.id = p5.parent_id
where 19 in (p1.parent_id,
p2.parent_id,
p3.parent_id,
p4.parent_id,
p5.parent_id,
p6.parent_id)
order by 1, 2, 3, 4, 5, 6, 7;
Voir ce violon
Le where
condition spécifie de quel parent vous souhaitez récupérer les descendants. Vous pouvez étendre cette requête avec plus de niveaux si nécessaire.