2009-03-18 8 views
9

Últimamente uso la mierda del Nested Set Model. He disfrutado diseñando consultas para casi cualquier operación y vista útil. Una cosa en la que estoy atascado es cómo seleccionar los hijos inmediatos (y solo los hijos, ¡no más descendientes!) De un nodo.¿Hay una manera simple de consultar a los hijos de un nodo?

Para ser sincero, sé de alguna manera, pero implica cantidades inmanejables de SQL. Estoy seguro de que hay una solución más directa.

Respuesta

9

¿Has leído el artículo que publicaste? Es bajo el título "Encuentra los subordinados inmediatos de un nodo"

SELECT node.name, (COUNT(parent.name) - (sub_tree.depth + 1)) AS depth 
FROM nested_category AS node, 
    nested_category AS parent, 
    nested_category AS sub_parent, 
    (
     SELECT node.name, (COUNT(parent.name) - 1) AS depth 
     FROM nested_category AS node, 
     nested_category AS parent 
     WHERE node.lft BETWEEN parent.lft AND parent.rgt 
     AND node.name = 'PORTABLE ELECTRONICS' 
     GROUP BY node.name 
     ORDER BY node.lft 
    )AS sub_tree 
WHERE node.lft BETWEEN parent.lft AND parent.rgt 
    AND node.lft BETWEEN sub_parent.lft AND sub_parent.rgt 
    AND sub_parent.name = sub_tree.name 
GROUP BY node.name 
HAVING depth <= 1 
ORDER BY node.lft; 

Sin embargo, lo que hago (esto es hacer trampa) es combiné el conjunto anidado con listas de adyacencia - lo integrar un "parent_id" en la tabla , entonces puedo preguntar fácilmente por los hijos de un nodo.

+0

"... He combinado el conjunto anidado con listas de adyacencia ..." Ja! Eso es lo que estoy haciendo. Me uno a un adj. vista de lista, basada en una consulta de Joe Celko. Simplemente parece una gran cantidad de código. Incluso la solución del artículo vinculado es ... verbosa. – Metaphile

+0

Es decir, comparar seleccionando _todos_ los descendientes de un nodo: SELECCIONAR * FROM nodes WHERE nodes.leftBound ENTRE parentLeftBound Y parentRightBound; – Metaphile

+0

Bueno, el "child_view" es bastante simple, SELECCIONE * FROM nodos DONDE parent_id = 123456: D –

7

Me parece que esto debería ser fácilmente realizable sin las subconsultas ni la redundancia de la columna padre. Por ejemplo, dada la izquierda y derecha de los padres ya se conocen:

SELECT child.id 
FROM nodes AS child 
LEFT JOIN nodes AS ancestor ON 
    ancestor.left BETWEEN @parentleft+1 AND @parentright-1 AND 
    child.left BETWEEN ancestor.left+1 AND ancestor.right-1 
WHERE 
    child.left BETWEEN @parentleft+1 AND @parentright-1 AND 
    ancestor.id IS NULL 

Es decir, “de todos los descendientes del nodo en cuestión, recoger los sin ancestro entre ellos y el nodo”.

+0

Me pregunto qué respuesta es mejor en el rendimiento, esta o la publicación aceptada. Sin embargo, ambas soluciones funcionan. Este se ve un poco más compacto. – andreas

+0

Para árboles muy grandes, encontramos que funciona mal en MySQL y peor en SQL Server porque requiere que se realice un bucle anidado en la base de datos. Cambiamos nuestro código para simplemente recuperar todos los descendientes, y luego podar solo a los niños en nuestro código de aplicación. – user393274

+0

@andreas Esto es muy similar a la respuesta aceptada, la diferencia es que en vez de contar los niños y filtrar a los que tienen 1 hijo, se filtra al ver si el antepasado es NULL. Eso significa que tiene menos trabajo que hacer (no ordenar y contar el paso). Debería ser más rápido, pero no lo he probado. – Ariel

5

este es mejor y más pequeño

usuario "bobince" casi lo tenía. Lo descubrí y lo hice funcionar porque tengo un poco más de experiencia en MySQL que la mayoría. Sin embargo, puedo ver por qué la respuesta de Bobince podría asustar a la gente. Su consulta es incompleta. Primero debe seleccionar parent_left y parent_right en las variables de mysql.

Las dos consultas siguientes se supone que su tabla se denomina tree, su columna de la izquierda se llama lft, columna derecha se llama rgt, y que la clave principal se denomina id. Cambie estos valores para satisfacer sus necesidades. Además, examine la primera declaración de selección. Verás que estoy buscando los descendientes inmediatos del nodo 5. Cambia el número 5 para buscar hijos del nodo que quieras.

Personalmente creo que esta es una consulta más esbelta, más sexy y más eficiente que las otras presentadas hasta ahora.

SELECT `lft`, `rgt` INTO @parent_left, @parent_right FROM efm_files WHERE `id` = 5; 
SELECT `child`.`id` 
FROM `tree` AS `child` 
LEFT JOIN `tree` AS `ancestor` ON 
    `ancestor`.`lft` BETWEEN @parent_left+1 AND @parent_right-1 AND 
    `child`.`lft` BETWEEN `ancestor`.`lft`+1 AND `ancestor`.`rgt`-1 
WHERE 
    `child`.`lft` BETWEEN @parent_left+1 AND @parent_right-1 AND 
    `ancestor`.`id` IS NULL 
+0

¿Qué es 'efm_files'? – Madbreaks

+0

efm_files es el nombre de una tabla en mi base de datos mysql. Reemplácelo con su propio nombre de tabla para su base de datos. – mrbinky3000

0

Yo sé que estoy haciendo un post necro, pero aquí es mi opinión.

¿por qué no incluir una columna de "profundidad" en su conjunto anidado? la columna de profundidad indicará el "nivel" de un elemento.

así, para seleccionar los niño inmediatas de un artículo, simplemente

select c.*
from tree as p
join tree as c on (c.left > p.left and c.right < p.right and c.depth = p.dept + 1) where p.id = @parentID

+0

Porque estrictamente hablando, este ya no es un conjunto anidado, es una combinación de modelos jerárquicos. Y la mayoría de las veces, cambiar el modelo para resolver un problema no es una opción. – Madbreaks

0

me gustaría ir con una columna de profundidad, también. Pero el uso

SELECT Child.Node, Child.LEFT, Child.RIGHT 
FROM Tree AS Child, Tree AS Parent 
WHERE 
     Child.Depth = Parent.Depth + 1 
     AND Child.LEFT > Parent.LEFT 
     AND Child.RIGHT < Parent.RIGHT 
     AND Parent.LEFT = 1 -- Given Parent Node Left Index 

Wikipedia

+0

Tenga en cuenta que requiere * columna de profundidad * adicional junto con el Id. Izquierdo y derecho. – Youngjae

0

me encontré Wikipedia link tiene buena versión minimizada de respuesta junto con la respuesta seleccionada.

SELECT DISTINCT Child.Name 
FROM ModelTable AS Child, ModelTable AS Parent 
WHERE Parent.Lft < Child.Lft AND Parent.Rgt > Child.Rgt -- associate Child Nodes with ancestors 
GROUP BY Child.Name 
HAVING MAX(Parent.Lft) = @parentId -- Subset for those with the given Parent Node as the nearest ancestor 

Y, cualquiera de ustedes tratan de expresarlo con LINQ, por favor siga el enlace: https://stackoverflow.com/a/25594386/361100

Cuestiones relacionadas