Remarque: cette question a été mise à jour pour refléter le fait que nous utilisons actuellement MySQL. Cela fait, j'aimerais voir combien il serait plus facile si nous passions à une base de données prenant en charge CTE.
J'ai une table d'auto-référencement avec une clé primaire id
et une clé étrangère parent_id
.
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| parent_id | int(11) | YES | | NULL | |
| name | varchar(255) | YES | | NULL | |
| notes | text | YES | | NULL | |
+------------+--------------+------+-----+---------+----------------+
Etant donné un name
, comment puis-je interroger le parent de niveau supérieur?
Étant donné un name
, comment puis-je interroger tous les id
associés à un enregistrement de name = 'foo'
?
contexte: je ne suis pas un dba, mais je prévois de demander à un dba d'implémenter ce type de structure hiérarchique et j'aimerais tester certaines requêtes. La motivation pour le faire est décrite par Kattge et al 2011 .
Voici un exemple des relations entre les identifiants de la table:
-- -----------------------------------------------------
-- Create a new database called 'testdb'
-- -----------------------------------------------------
SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL';
CREATE SCHEMA IF NOT EXISTS `testdb` DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci ;
USE `testdb` ;
-- -----------------------------------------------------
-- Table `testdb`.`observations`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `testdb`.`observations` (
`id` INT NOT NULL ,
`parent_id` INT NULL ,
`name` VARCHAR(45) NULL ,
PRIMARY KEY (`id`) )
ENGINE = InnoDB;
SET SQL_MODE=@OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;
-- -----------------------------------------------------
-- Add Example Data Set
-- -----------------------------------------------------
INSERT INTO observations VALUES (1,3), (2,5), (3,NULL), (4,10),
(5,NULL), (6,1), (7,5), (8,10), (9,10), (10,3);
la source
Réponses:
Vous devez absolument créer un script via MySQL Stored Procedure Language
Voici une fonction stockée appelée
GetParentIDByID
pour récupérer un parentID avec un identifiant à rechercherVoici une fonction stockée appelée
GetAncestry
pour extraire une liste d'ID parents à partir de la première génération, ainsi que toute la hiérarchie en fonction d'un identifiant pour commencer:Voici quelque chose pour générer des exemples de données:
Voici ce que cela génère:
Voici ce que les fonctions génèrent pour chaque valeur:
MORAL OF THE STORY: La récupération récursive de données doit être scriptée dans MySQL
MISE À JOUR 2011-10-24 17:17 EDT
Voici l'inverse de GetAncestry. Je l'appelle GetFamilyTree.
Voici l'algorithme:
Je crois que dans mes cours sur les structures de données et les algorithmes à College, on parle de quelque chose comme une traversée d’arbres de pré-ordre / préfixe.
Voici le code:
Voici ce que chaque rangée produit
Cet algorithme fonctionne proprement s'il n'y a pas de chemins cycliques. S'il existe des chemins cycliques, vous devez ajouter une colonne "visité" à la table.
Une fois que vous avez ajouté la colonne visitée, voici l'algorithme bloquant les relations cycliques:
MISE À JOUR 2011-10-24 17:37 EDT
J'ai créé un nouveau tableau appelé observations et rempli vos exemples de données. J'ai changé les procédures stockées pour utiliser des observations au lieu de pctable. Voici votre sortie:
MISE À JOUR 2011-10-24 18:22 EDT
J'ai changé le code pour GetAncestry. Il y avait
WHILE ch > 1
il devrait êtreWHILE ch > 0
Essayez-le maintenant !!!
la source
Obtenir tous les parents d'un nœud spécifié:
Pour obtenir le nœud racine, vous pouvez par exemple
ORDER BY level
et prendre la première ligneObtenir tous les enfants d'un nœud spécifié:
(notez la condition permutée pour la jointure dans la partie récursive de l'instruction)
A ma connaissance, les SGBD suivants prennent en charge les CTE récursifs:
Modifier
Sur la base de vos exemples de données, les éléments suivants extrairaient tous les sous-arbres de la table, y compris le chemin complet de chaque nœud en tant que colonne supplémentaire:
La sortie serait la suivante:
la source
La fonction GetFamilyTree dans la réponse de Rolando ne fonctionne pas lorsque l'id donné est supérieur à 4, car la fonction FORMAT MySQL ajoute des virgules pour des séparateurs de milliers. J'ai modifié la fonction stockée GetFamilyTree pour qu'elle fonctionne avec les grands identificateurs d'entiers, comme ci-dessous:
front_id défini dans if if loop.
la source