2008-10-27 19 views
24

Digamos que tengo el siguiente cuadro:¿Cómo creo una consulta recursiva en MSSQL 2005?

CustomerID ParentID Name 
========== ======== ==== 
1   null  John 
2   1  James 
3   2  Jenna 
4   3  Jennifer 
5   3  Peter 
6   5  Alice 
7   5  Steve 
8   1  Larry 

que desea recuperar en una consulta a todos los descendientes de James (Jenna, Jennifer, Peter, Alice, Steve). Gracias, Pablo.

+0

En Lo RDBMS la solución debe funcionar? Si es Oracle, aprenda acerca de CONNECT BY PREVIEW – Salamander2007

+0

lo siento, se olvidó de mencionar, en MSSQL 2005 –

Respuesta

34

En SQL Server 2005 se puede utilizar CTEs (Common Table Expressions):

with Hierachy(CustomerID, ParentID, Name, Level) 
as 
(
select CustomerID, ParentID, Name, 0 as Level 
    from Customers c 
    where c.CustomerID = 2 -- insert parameter here 
    union all 
    select c.CustomerID, c.ParentID, c.Name, ch.Level + 1 
    from Customers c 
    inner join Hierachy ch 
    on c.ParentId = ch.CustomerID 
) 
select CustomerID, ParentID, Name 
from Hierachy 
where Level > 0 
+0

Si no desea utilizar la recursión o necesita obtener el elemento primario de un hijo, puede implementar la solución del artículo de Rob Volk en sqlteam: http://www.sqlteam.com/article/more-trees-hierarchies- en-sql – nzic

0

No puede hacer recursiones en SQL sin procedimientos almacenados. La forma de resolver esto es usar conjuntos anidados, básicamente modelan un árbol en SQL como un conjunto.

Tenga en cuenta que esto requerirá un cambio en el modelo de datos actual o posiblemente averiguar cómo crear una vista en el modelo original.

PostgreSQL (usando muy pocas extensiones PostgreSQL, simplemente SERIAL y ON COMMIT DROP, la mayoría de RDBMSes tendrán una funcionalidad similar):

Configuración:

CREATE TABLE objects(
    id SERIAL PRIMARY KEY, 
    name TEXT, 
    lft INT, 
    rgt INT 
); 

INSERT INTO objects(name, lft, rgt) VALUES('The root of the tree', 1, 2); 

Adición de un niño:

START TRANSACTION; 

-- postgresql doesn't support variables so we create a temporary table that 
-- gets deleted after the transaction has finished. 

CREATE TEMP TABLE left_tmp(
    lft INT 
) ON COMMIT DROP; -- not standard sql 

-- store the left of the parent for later use 
INSERT INTO left_tmp (lft) VALUES((SELECT lft FROM objects WHERE name = 'The parent of the newly inserted node')); 

-- move all the children already in the set to the right 
-- to make room for the new child 
UPDATE objects SET rgt = rgt + 2 WHERE rgt > (SELECT lft FROM left_tmp LIMIT 1); 
UPDATE objects SET lft = lft + 2 WHERE lft > (SELECT lft FROM left_tmp LIMIT 1); 

-- insert the new child 
INSERT INTO objects(name, lft, rgt) VALUES(
    'The name of the newly inserted node', 
    (SELECT lft + 1 FROM left_tmp LIMIT 1), 
    (SELECT lft + 2 FROM left_tmp LIMIT 1) 
); 

COMMIT; 

Mostrar un rastro de abajo hacia arriba:

SELECT 
    parent.id, parent.lft 
FROM 
    objects AS current_node 
INNER JOIN 
    objects AS parent 
ON 
    current_node.lft BETWEEN parent.lft AND parent.rgt 
WHERE 
    current_node.name = 'The name of the deepest child' 
ORDER BY 
    parent.lft; 

mostrar todo el árbol:

SELECT 
    REPEAT(' ', CAST((COUNT(parent.id) - 1) AS INT)) || '- ' || current_node.name AS indented_name 
FROM 
    objects current_node 
INNER JOIN 
    objects parent 
ON 
    current_node.lft BETWEEN parent.lft AND parent.rgt 
GROUP BY 
    current_node.name, 
    current_node.lft 
ORDER BY 
    current_node.lft; 

Seleccionar todo abajo de un cierto elemento del árbol:

SELECT 
    current_node.name AS node_name 
FROM 
    objects current_node 
INNER JOIN 
    objects parent 
ON 
    current_node.lft BETWEEN parent.lft AND parent.rgt 
AND 
    parent.name = 'child' 
GROUP BY 
    current_node.name, 
    current_node.lft 
ORDER BY 
    current_node.lft; 
+2

Pero puede, vea la respuesta de mathieu. –

+0

El código que publiqué usa una técnica aplicable a cualquier base de datos ANSI-SQL porque el OP olvidó mencionar el RDBMS que estaba usando en su publicación original (vea los comentarios para esa publicación). –

-9

A menos Me falta algo, la recursividad no es necesaria ...

SELECT d.NAME FROM Customers As d 
INNER JOIN Customers As p ON p.CustomerID = d.ParentID 
WHERE p.Name = 'James' 
+0

no obtendrás a Jennifer, Peter, Alice, Steve de esta manera – mathieu

3

Por toda respuesta, de abajo hacia arriba utilización de Mathieu con una pequeña modificación:



with Hierachy(CustomerID, ParentID, Name, Level) 
as 
(
select CustomerID, ParentID, Name, 0 as Level 
    from Customers c 
    where c.CustomerID = 2 -- insert parameter here 
    union all 
    select c.CustomerID, c.ParentID, c.Name, ch.Level + 1 
    from Customers c 
    inner join Hierachy ch 

    -- EDITED HERE -- 
    on ch.ParentId = c.CustomerID 
    ----------------- 

) 
select CustomerID, ParentID, Name 
from Hierachy 
where Level > 0 


Cuestiones relacionadas