Pergunta

Vamos dizer que eu tenho a seguinte tabela:

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 

Eu quero recuperar em uma consulta todos os descendentes de James (Jenna, Jennifer, Pedro, Alice, Steve). Obrigado, Pablo.

Foi útil?

Solução

No SQL Server 2005, você pode usar CTEs (expressões de tabela comum) :

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

Outras dicas

Como resposta inferior uso até de Mathieu com uma pequena modificação:



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


Você não pode fazer recursão em SQL sem procedimentos armazenados. A maneira de resolver isso é usando conjuntos aninhados, eles basicamente modelar uma árvore em SQL como um conjunto.

Observe que isso irá exigir uma alteração para o modelo de dados atual ou, possivelmente, descobrir como criar uma visão sobre o modelo original.

PostgreSQL exemplo (usando muito poucas extensões do PostgreSQL, apenas SERIAL e ON COMMIT GOTA, a maioria dos RDBMSes terá uma funcionalidade semelhante):

Configuração:

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);

Adicionando uma criança:

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;

Mostra uma trilha de baixo para cima:

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 a árvore inteira:

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;

Selecionar tudo para baixo a partir de um certo elemento da árvore:

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;

A menos que eu estou faltando alguma coisa, a recursão não é necessário ...

SELECT d.NAME FROM Customers As d
INNER JOIN Customers As p ON p.CustomerID = d.ParentID
WHERE p.Name = 'James'
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top