nota: esta pregunta se ha actualizado para reflejar que actualmente estamos usando MySQL, después de hacerlo, me gustaría ver cuánto más fácil sería si cambiamos a una base de datos compatible con CTE.
Tengo una tabla de autorreferencia con una clave principal id
y una clave externa 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 | |
+------------+--------------+------+-----+---------+----------------+
Dado a name
, ¿cómo puedo consultar al padre de nivel superior?
Dado a name
, ¿cómo puedo consultar todos los id
asociados con un registro de name = 'foo'
?
contexto: no soy un dba, pero estoy planeando pedirle a un dba que implemente este tipo de estructura jerárquica y me gustaría probar algunas consultas. Kattge et al . 2011 describen la motivación para hacerlo .
Aquí hay un ejemplo de las relaciones entre los identificadores en la tabla:
-- -----------------------------------------------------
-- 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);
mysql
postgresql
hierarchy
David LeBauer
fuente
fuente
Respuestas:
Definitivamente tiene que escribir esto a través del lenguaje de procedimiento almacenado MySQL
Aquí hay una función almacenada llamada
GetParentIDByID
para recuperar un ParentID dado un ID para buscarAquí hay una función almacenada llamada
GetAncestry
para recuperar una lista de ParentID a partir de la primera generación hasta la jerarquía dada una ID para comenzar:Aquí hay algo para generar datos de muestra:
Esto es lo que genera:
Esto es lo que generan las funciones para cada valor:
MORAL DE LA HISTORIA: La recuperación de datos recursivos debe estar programada en MySQL
ACTUALIZACIÓN 2011-10-24 17:17 EDT
Aquí está el reverso de GetAncestry. Lo llamo GetFamilyTree.
Aquí está el algoritmo:
Creo que de mis clases de estructuras de datos y algoritmos en la universidad, esto se llama algo así como el recorrido del árbol de preorden / prefijo.
Aquí está el código:
Esto es lo que produce cada fila
Este algoritmo funciona de manera limpia siempre que no haya caminos cíclicos. Si existe alguna ruta cíclica, deberá agregar una columna 'visitada' a la tabla.
Una vez que agregue la columna visitada, aquí está el algoritmo que bloquea las relaciones cíclicas:
ACTUALIZACIÓN 2011-10-24 17:37 EDT
Creé una nueva tabla llamada observaciones y completé sus datos de muestra. Cambié los procedimientos almacenados para usar observaciones en lugar de ptable. Aquí está su salida:
ACTUALIZACIÓN 2011-10-24 18:22 EDT
Cambié el código para GetAncestry. Hubo
WHILE ch > 1
que debería serWHILE ch > 0
Pruebalo ahora !!!
fuente
Obtener todos los padres de un nodo específico:
Para obtener el nodo raíz, puede, por ejemplo,
ORDER BY level
y tomar la primera filaObtener todos los hijos de un nodo específico:
(tenga en cuenta la condición intercambiada para la unión en la parte recursiva de la declaración)
Que yo sepa, los siguientes DBMS admiten CTE recursivos:
Editar
Según sus datos de muestra, lo siguiente recuperaría todos los subárboles de la tabla, incluida la ruta completa para cada nodo como una columna adicional:
El resultado sería este:
fuente
La función GetFamilyTree en la respuesta de Rolando no funciona cuando la identificación dada es más de 4 enteros, porque la función FORMAT MySQL agrega comas para miles de separadores. Modifiqué la función almacenada GetFamilyTree para que funcione con identificadores enteros grandes como se muestra a continuación:
front_id definido dentro del bucle if else.
fuente