query - ¿Cómo creo una consulta recursiva en MSSQL 2005?
sql count group by (4)
A menos que me falta algo, la recursión no es necesaria ...
SELECT d.NAME FROM Customers As d
INNER JOIN Customers As p ON p.CustomerID = d.ParentID
WHERE p.Name = ''James''
Digamos que tengo la siguiente tabla:
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
Quiero recuperar en una consulta a todos los descendientes de James (Jenna, Jennifer, Peter, Alice, Steve). Gracias, Pablo.
En SQL Server 2005 puede usar CTE (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
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.
Ejemplo de Postgresql (usando muy pocas extensiones postgresql, solo SERIAL y ON COMMIT DROP, la mayoría de RDBMS tendrán una funcionalidad similar):
Preparar:
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);
Agregar 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;
Muestre 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 el árbol completo:
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;
Seleccione todo desde 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;
Para abajo, usa la respuesta 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