J'ai une table MySQL qui se présente comme suit:
id | name | parent_id
19 | category1 | 0
20 | category2 | 19
21 | category3 | 20
22 | category4 | 21
......
Maintenant, je veux avoir une seule requête MySQL à laquelle je fournis simplement l'identifiant [par exemple, dire 'id = 19'] alors je devrais obtenir tous ses identifiants enfants [c'est-à-dire que le résultat devrait avoir les identifiants '20, 21,22 ']. ... Aussi, la hiérarchie des enfants n'est pas connue elle peut varier ....
De plus, j'ai déjà la solution en utilisant la boucle for ..... Faites-moi savoir comment obtenir la même chose en utilisant une seule requête MySQL si possible.
mysql
sql
hierarchical-data
recursive-query
Tarun Parswani
la source
la source
Réponses:
Pour MySQL 8+: utilisez la
with
syntaxe récursive .Pour MySQL 5.x: utilisez des variables en ligne, des ID de chemin ou des auto-jointures.
MySQL 8+
La valeur spécifiée dans
parent_id = 19
doit être définie sur celleid
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 obtiendriez cela avec la requête suivante:
Voici un violon .
Ici, la valeur spécifiée dans
@pv := '19'
doit être définie sur celleid
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 variables dans une requête
Cette requête utilise une syntaxe MySQL spécifique: les variables sont affectées et modifiées lors de son exécution. Certaines hypothèses sont émises sur l'ordre d'exécution:
from
clause est évaluée en premier. C'est donc là que@pv
s'initialise.where
clause est évaluée pour chaque enregistrement dans l'ordre de récupération à partir desfrom
alias. C'est donc là qu'une condition est mise pour n'inclure que les enregistrements pour lesquels le parent a déjà été identifié comme étant dans l'arbre descendant (tous les descendants du parent principal sont progressivement ajoutés@pv
).where
clause sont évaluées dans l'ordre et l'évaluation est interrompue une fois que le résultat global est certain. Par conséquent, la deuxième condition doit être à la deuxième place, car elle ajoute leid
à la liste parent, et cela ne doit se produire que si leid
passe la première condition. Lalength
fonction est uniquement appelée pour s'assurer que cette condition est toujours vraie, même si lapv
chaîne donnerait pour une raison quelconque une valeur fausse.Dans l'ensemble, on peut trouver ces hypothèses trop risquées pour être fiables. La documentation met en garde:
Ainsi, même s'il 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 grande. C'est une "fonctionnalité" qui sera supprimée dans une future version de MySQL :
Comme indiqué ci-dessus, à partir de MySQL 8.0, vous devez utiliser la
with
syntaxe récursive .Efficacité
Pour les ensembles de données très volumineux, cette solution peut devenir lente, car l'
find_in_set
opération 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 dans le 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 syntaxe standard SQL: 1999 ISO
WITH [RECURSIVE]
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 à partir de la version 8.0, MySQL le prend également en charge . Voir le haut 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 la
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 celle qui le fait, c'est certainement la meilleure option. Sinon, envisagez également les alternatives suivantes.
Alternative 2: identificateurs de style de chemin
Les choses deviennent beaucoup plus faciles si vous affectez des
id
valeurs qui contiennent les informations hiérarchiques: un chemin. Par exemple, dans votre cas, cela pourrait ressembler à ceci:Alors votre
select
ressemblerait à ceci:Alternative 3: auto-jointures répétées
Si vous connaissez une limite supérieure pour la profondeur de votre arbre de hiérarchie, vous pouvez utiliser une
sql
requête standard comme celle-ci:Voir ce violon
La
where
condition spécifie le parent dont vous souhaitez récupérer les descendants. Vous pouvez étendre cette requête avec plus de niveaux si nécessaire.la source
parent_id > id
vous ne pouvez pas utiliser cette solution.WITH RECURSIVE
méthode, j'ai trouvé l'article suivant vraiment utile avec différents scénarios tels que la profondeur de récursivité, les cycles distincts et de détection et de fermetureDepuis le blog Gestion des données hiérarchiques dans MySQL
Structure de la table
Requete:
Production
La plupart des utilisateurs à un moment ou à un autre ont traité des données hiérarchiques dans une base de données SQL et ont sans aucun doute appris que la gestion des données hiérarchiques n'est pas ce à quoi une base de données relationnelle est destinée. Les tables d'une base de données relationnelle ne sont pas hiérarchiques (comme XML), mais sont simplement une liste plate. Les données hiérarchiques ont une relation parent-enfant qui n'est pas naturellement représentée dans une table de base de données relationnelle. Lire la suite
Consultez le blog pour plus de détails.
ÉDITER:
Production:
Référence: Comment faire la requête SELECT récursive dans Mysql?
la source
Essayez-les:
Définition du tableau:
Lignes expérimentales:
Procédure stockée récursive:
Fonction wrapper pour la procédure stockée:
Sélectionnez un exemple:
Production:
Filtrage des lignes avec un certain chemin:
Production:
la source
(20, 'category2', 19), (21, 'category3', 20), (22, 'category4', 20),
La meilleure approche que j'ai trouvée est
Approche de lignée descr. peut être trouvé partout, par exemple ici ou ici . En fonction - c'est ce qui m'a inspiré.
En fin de compte, la solution est devenue plus ou moins simple, relativement rapide et SIMPLE.
Corps de la fonction
Et puis vous venez
J'espère que cela aide quelqu'un :)
la source
A fait la même chose pour une autre quetion ici
Mysql select recursive get all child with multiple level
La requête sera:
la source
SELECT idFolder, (SELECT GROUP_CONCAT(lv SEPARATOR ',') FROM ( SELECT @pv:=(SELECT GROUP_CONCAT(idFolder SEPARATOR ',') FROM Folder WHERE idFolderParent IN (@pv)) AS lv FROM Folder JOIN (SELECT @pv:= F1.idFolder )tmp WHERE idFolderParent IN (@pv)) a) from folder F1 where id > 10
; Je ne peux pas référencer F1.idFolder pour @pvNULL
. Savez-vous pourquoi cela pourrait être? Y a-t-il des prérequis en termes de moteur de base de données, ou quelque chose a changé depuis que vous avez fait cette réponse qui rend cette requête obsolète?Si vous avez besoin d'une vitesse de lecture rapide, la meilleure option est d'utiliser une table de fermeture. Une table de fermeture contient une ligne pour chaque paire ancêtre / descendant. Donc, dans votre exemple, la table de fermeture ressemblerait à
Une fois que vous avez ce tableau, les requêtes hiérarchiques deviennent très faciles et rapides. Pour obtenir tous les descendants de la catégorie 20:
Bien sûr, il y a un gros inconvénient lorsque vous utilisez des données dénormalisées comme celle-ci. Vous devez maintenir la table de fermeture à côté de votre table des catégories. La meilleure façon est probablement d'utiliser des déclencheurs, mais il est quelque peu complexe de suivre correctement les insertions / mises à jour / suppressions pour les tables de fermeture. Comme pour tout, vous devez examiner vos besoins et décider quelle approche vous convient le mieux.
Modifier : voir la question Quelles sont les options de stockage des données hiérarchiques dans une base de données relationnelle? pour plus d'options. Il existe différentes solutions optimales pour différentes situations.
la source
Requête simple pour lister l'enfant de la première récursivité:
Résultat:
... avec jointure gauche:
La solution de @tincot pour lister tous les enfants:
Testez-le en ligne avec Sql Fiddle et voyez tous les résultats.
http://sqlfiddle.com/#!9/a318e3/4/0
la source
Vous pouvez le faire comme ça dans d'autres bases de données assez facilement avec une requête récursive (YMMV sur les performances).
L'autre façon de le faire est de stocker deux bits de données supplémentaires, une valeur gauche et droite. Les valeurs gauche et droite sont dérivées d'un parcours de pré-commande de la structure arborescente que vous représentez.
Ceci est connu sous le nom de Traversal d'arbre de précommande modifié et vous permet d'exécuter une requête simple pour obtenir toutes les valeurs parentes en même temps. Il porte également le nom de "jeu imbriqué".
la source
Utilisez simplement la classe php BlueM / tree pour créer un arbre d'une table d'auto-relation dans mysql.
Voici un exemple d'utilisation de BlueM / tree:
la source
C'est un tableau des catégories .
Production::
la source
C'est un peu délicat, vérifiez si cela fonctionne pour vous
Lien SQL fiddle http://www.sqlfiddle.com/#!2/e3cdf/2
Remplacez par votre nom de champ et de table de manière appropriée.
la source
Quelque chose qui n'est pas mentionné ici, bien qu'un peu similaire à la deuxième alternative de la réponse acceptée, mais différent et à faible coût pour les requêtes de grande hiérarchie et les éléments faciles (insérer la mise à jour), ajouterait une colonne de chemin persistant pour chaque élément.
certains, comme:
Exemple:
Optimisez la longueur du chemin et
ORDER BY path
utilisez le codage base36 à la place de l'identifiant de chemin numérique réelhttps://en.wikipedia.org/wiki/Base36
Suppression également du séparateur de barre oblique «/» en utilisant une longueur fixe et un remplissage à l'ID codé
Explication détaillée de l'optimisation ici: https://bojanz.wordpress.com/2014/04/25/storing-hierarchical-data-materialized-path/
FAIRE
construction d'une fonction ou d'une procédure pour diviser le chemin d'accès pour retrouver les ancêtres d'un élément
la source
base36
Cela fonctionne pour moi, j'espère que cela fonctionnera aussi pour vous. Il vous donnera un jeu d'enregistrements Root to Child pour tout menu spécifique. Modifiez le nom du champ selon vos besoins.
la source
Je l'ai trouvé plus facilement pour:
1) créer une fonction qui vérifiera si un élément se trouve n'importe où dans la hiérarchie parent d'un autre. Quelque chose comme ça (je n'écrirai pas la fonction, faites-la avec WHILE DO):
dans votre exemple
2) utilisez une sous-sélection, quelque chose comme ceci:
la source
J'ai fait une requête pour vous. Cela vous donnera une catégorie récursive avec une seule requête:
Voici un violon .
la source