2009-06-13 12 views
15

Me gustaría obtener todas las ID de niños en un árbol con MySQL solamente.¿Cómo encontrar todas las identificaciones de niños recursivamente?

Tengo una tabla como la siguiente:

ID parent_id name 
1 0   cat1 
2 1   subcat1 
3 2   sub-subcat1 
4 2   sub-subcat2 
5 0   cat2 

Ahora estoy tratando de conseguir todos los ID de niño para cat1 (2,3,4) de forma recursiva. ¿Hay alguna forma de cómo lograr eso?

Respuesta

15

Existen dos métodos básicos para hacer esto: listas de adyacencia y listas anidadas. Eche un vistazo al Managing Hierarchical Data in MySQL.

Lo que tienes es una lista de adyacencias. No, no hay una forma de agarrar recursivamente a todos los descendientes con una sola instrucción SQL. Si es posible, solo agárralos a todos y correlaciona todos en código.

Los conjuntos anidados pueden hacer lo que usted desee, pero tiendo a evitarlo porque el costo de insertar un registro es alto y es propenso a errores.

+0

Hay una manera de tener la recursividad en una consulta SQL sin formato en los RDBMS que lo apoyan, como PostgreSQL – shesek

+0

Esta página web de devolución de enlace no está disponible – HMagdy

1

Probablemente podría hacerlo con un procedimiento almacenado, si esa es una opción para usted.

De lo contrario, no puede hacerlo con un solo estado sql.

Lo ideal es hacer que las llamadas recursivas a recorrer el árbol de su programa

+0

Sí, pero eso podría matar el rendimiento si el árbol es grande – dm76

-1

Su pregunta parece un poco impreciso. ¿Por qué quieres tenerlos, y qué quieres decir con tenerlos, "en un árbol"?

La tabla que tiene IS (la forma relacional para representar) el árbol.

Si los quiere "en una tabla" con filas que contienen los pares (ID 4, ParentID 0), entonces necesita la versión de SQL recursiva de su motor SQL para hacer esto, si ese motor lo admite.

No conocería específicamente MySQL, pero tengo entendido que una vez planearon implementar SQL recursivo utilizando la misma sintaxis que Oracle, es decir, con CONNECT BY.

Si mira en la tabla de contenido de su manual palabras clave como "consultas recursivas" o "CONNECT BY", me imagino que debería poder encontrar la respuesta.

(Lo siento por no ser capaz de proporcionar una mayor lista para consumir respuesta.)

0

Al ver que la respuesta es, básicamente, no o al menos no muy fácil con una sola instrucción MySQL, voy a publicar mi código php/mysql para hacer la lista jerarquía ..

function createCategorySubArray() 
{ 
    $categories = getSQL("SELECT pos_category_id FROM pos_categories"); 
    for($i=0;$i<sizeof($categories);$i++) 
    { 
     //here we need to find all sub categories 
     $pos_category_id = $categories[$i]['pos_category_id']; 
     $cat_list[$pos_category_id] = recursiveCategory($pos_category_id,array()); 

    } 
    return $cat_list; 

} 
function recursiveCategory($pos_category_id, $array) 
{ 
    $return = getSql("SELECT pos_category_id FROM pos_categories WHERE parent = $pos_category_id"); 
    for($i=0;$i<sizeof($return);$i++) 
    { 
     $sub_cat = $return[$i]['pos_category_id']; 
     $array[] = $sub_cat; 
     $array = recursiveCategory($sub_cat, $array); 
    } 
    return $array; 
} 

Entonces llaman por $ cat_array = createCategorySubArray();

Necesito saber qué promociones basadas en categorías de productos se están aplicando a las subcategorías.

1

crear la tabla se debe ver como a continuación

DROP TABLE IF EXISTS `parent_child`; 
CREATE TABLE `parent_child` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `name` varchar(255) DEFAULT NULL, 
    `parent_id` int(11) DEFAULT NULL, 
    PRIMARY KEY (`id`) 
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=latin1; 

insert into `parent_child`(`id`,`name`,`parent_id`) 
values (1,'cat1',0),(2,'subcat1',1), 
(3,'sub-subcat1',2),(4,'sub-subcat2',2), 
(5,'cat2',0); 

Crear función para conseguir elemento hijo de padres

DELIMITER $$ 

USE `yourdatabase`$$ 

DROP FUNCTION IF EXISTS `GetAllNode1`$$ 

CREATE DEFINER=`root`@`localhost` FUNCTION `GetAllNode1`(GivenID INT) RETURNS TEXT CHARSET latin1 
    DETERMINISTIC 
BEGIN 
    DECLARE rv,q,queue,queue_children TEXT; 
    DECLARE queue_length,front_id,pos INT; 
    SET rv = ''; 
    SET queue = GivenID; 
    SET queue_length = 1; 
    WHILE queue_length > 0 DO 
     SET front_id = queue; 
     IF queue_length = 1 THEN 
      SET queue = ''; 
     ELSE 
      SET pos = LOCATE(',',queue) + 1; 
      SET q = SUBSTR(queue,pos); 
      SET queue = q; 
     END IF; 
     SET queue_length = queue_length - 1; 
     SELECT IFNULL(qc,'') INTO queue_children 
     FROM (SELECT GROUP_CONCAT(id) AS qc 
     FROM `parent_child` WHERE `parent_id` = front_id) A ; 
     IF LENGTH(queue_children) = 0 THEN 
      IF LENGTH(queue) = 0 THEN 
       SET queue_length = 0; 
      END IF; 
     ELSE 
      IF LENGTH(rv) = 0 THEN 
       SET rv = queue_children; 
      ELSE 
       SET rv = CONCAT(rv,',',queue_children); 
      END IF; 
      IF LENGTH(queue) = 0 THEN 
       SET queue = queue_children; 
      ELSE 
       SET queue = CONCAT(queue,',',queue_children); 
      END IF; 
      SET queue_length = LENGTH(queue) - LENGTH(REPLACE(queue,',','')) + 1; 
     END IF; 
    END WHILE; 
    RETURN rv; 
END$$ 

DELIMITER ; 

consulta de escritura para la salida de deseo

SELECT GetAllNode1(id) FROM parent_child 
or 
SELECT GetAllNode1(id) FROM parent_child where id =1 //for specific parent's child element 
6

Aquí es un simple de una sola consulta MySQL-solución:

SELECT GROUP_CONCAT(Level SEPARATOR ',') FROM (
    SELECT @Ids := (
     SELECT GROUP_CONCAT(`ID` SEPARATOR ',') 
     FROM `table_name` 
     WHERE FIND_IN_SET(`parent_id`, @Ids) 
    ) Level 
    FROM `table_name` 
    JOIN (SELECT @Ids := <id>) temp1 
) temp2 

Sólo sustituir <id> con el elemento de matriz de ID.

Esto devolverá una cadena con los ID s de todos los descendientes del elemento con ID = <id>, separadas por ,. Si se quiere tener múltiples filas devueltas, con un descendiente en cada fila, se puede usar algo como esto:

SELECT * 
FROM `table_name` 
WHERE FIND_IN_SET(`ID`, (
    SELECT GROUP_CONCAT(Level SEPARATOR ',') FROM (
     SELECT @Ids := (
      SELECT GROUP_CONCAT(`ID` SEPARATOR ',') 
      FROM `table_name` 
      WHERE FIND_IN_SET(`parent_id`, @Ids) 
    ) Level 
     FROM `table_name` 
     JOIN (SELECT @Ids := <id>) temp1 
    ) temp2 
)) 

Incluyendo el elemento raíz/padre

El PO pidió a los hijos de una elemento, que se responde arriba. En algunos casos, puede ser útil incluir el elemento raíz/principal en el resultado. Aquí están mis soluciones sugeridas:

cadena separada por comas de identificadores:

SELECT GROUP_CONCAT(Level SEPARATOR ',') FROM (
    SELECT <id> Level 
    UNION 
    SELECT @Ids := (
     SELECT GROUP_CONCAT(`ID` SEPARATOR ',') 
     FROM `table_name` 
     WHERE FIND_IN_SET(`parent_id`, @Ids) 
    ) Level 
    FROM `table_name` 
    JOIN (SELECT @Ids := <id>) temp1 
) temp2 

múltiples filas:

SELECT * 
FROM `table_name` 
WHERE `ID` = <id> OR FIND_IN_SET(`ID`, (
    SELECT GROUP_CONCAT(Level SEPARATOR ',') FROM (
     SELECT @Ids := (
      SELECT GROUP_CONCAT(`ID` SEPARATOR ',') 
      FROM `table_name` 
      WHERE FIND_IN_SET(`parent_id`, @Ids) 
    ) Level 
     FROM `table_name` 
     JOIN (SELECT @Ids := <id>) temp1 
    ) temp2 
)) 
+0

¿Puede explicar su solución? Parece muy prometedor, pero no estoy seguro de haberlo entendido. ¿@Ids es una función/var de alias utilizada recursivamente para unir y tiene más información sobre qué versiones de MySQL soportan este tipo de consulta o no? – MonkeyMonkey

+0

@Ids es una [variable definida por el usuario] (http://dev.mysql.com/doc/refman/5.7/en/user-variables.html), que existe en MySQL desde hace mucho tiempo. Creo que la consulta debe ejecutarse en cualquier versión de MySQL que desee usar. La consulta funciona al concatenar los identificadores de todos los elementos que son elementos secundarios del elemento original en la primera fila, luego concatena los identificadores de todos los elementos que son hijos de cualquier elemento incluido en la primera fila en la segunda fila, etc. En el fin, todas las filas se concatenan en una sola fila. Opcionalmente, esa fila se puede dividir para que cada elemento tenga su propia fila (última consulta). –

+1

Esto es genial y brillante, lo usamos por un tiempo, pero hoy me di cuenta de que los hijos del último nivel anidado están truncados: debe haber 'O FIND_IN_SET (id, @Ids)' en la cláusula 'WHERE'. ¡Gracias! – Wirone

Cuestiones relacionadas