문제

다음 표가 있다고 가정 해 봅시다.

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 

제임스 (Jenna, Jennifer, Peter, Alice, Steve)의 모든 후손들을 한 질문에서 검색하고 싶습니다. 감사합니다, 파블로.

도움이 되었습니까?

해결책

SQL Server 2005에서 사용할 수 있습니다 CTES (공통 테이블 표현) :

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

다른 팁

하단의 경우 약간 수정 된 Mathieu의 답변을 사용하십시오.



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


저장 절차 없이는 SQL에서 재귀를 할 수 없습니다. 이것을 해결하는 방법은 중첩 세트를 사용하는 것입니다. 기본적으로 SQL의 트리를 세트로 모델링합니다.

이를 위해서는 현재 데이터 모델을 변경하거나 원래 모델에서보기를 만드는 방법을 알아 내야합니다.

PostgreSQL 예제 (매우 적은 PostgreSQL 확장자 사용, 직렬 및 커밋 드롭을 사용하면 대부분의 RDBMSE는 비슷한 기능을 갖습니다).

설정:

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

아이 추가 :

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;

아래에서 위로 트레일을 표시합니다.

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;

전체 트리를 표시하십시오.

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;

트리의 특정 요소에서 모든 것을 선택하십시오.

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;

내가 뭔가를 놓치지 않으면 재귀가 필요하지 않습니다 ...

SELECT d.NAME FROM Customers As d
INNER JOIN Customers As p ON p.CustomerID = d.ParentID
WHERE p.Name = 'James'
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top