[mysql] MySQL에서 재귀 SELECT 쿼리를 수행하는 방법은 무엇입니까?

다음 표가 있습니다.

col1 | col2 | col3
-----+------+-------
1    | a    | 5
5    | d    | 3
3    | k    | 7
6    | o    | 2
2    | 0    | 8

“1”에 대한 사용자 검색이는 볼 것이다 프로그램 경우 col1가 “1”그 때의 값을 얻을 것이다 col3“5”를 다음 프로그램에서 “5”를 검색 계속 col1그것은 “3”을 얻을 것이다 에서 col3등. 따라서 다음과 같이 출력됩니다.

1   | a   | 5
5   | d   | 3
3   | k   | 7

사용자가 “6”을 검색하면 다음과 같이 인쇄됩니다.

6   | o   | 2
2   | 0   | 8

이를 위해 SELECT쿼리를 작성하는 방법은 무엇입니까?



답변

편집하다

@leftclickben이 언급 한 솔루션도 효과적입니다. 저장 프로 시저를 사용할 수도 있습니다.

CREATE PROCEDURE get_tree(IN id int)
 BEGIN
 DECLARE child_id int;
 DECLARE prev_id int;
 SET prev_id = id;
 SET child_id=0;
 SELECT col3 into child_id
 FROM table1 WHERE col1=id ;
 create TEMPORARY  table IF NOT EXISTS temp_table as (select * from table1 where 1=0);
 truncate table temp_table;
 WHILE child_id <> 0 DO
   insert into temp_table select * from table1 WHERE col1=prev_id;
   SET prev_id = child_id;
   SET child_id=0;
   SELECT col3 into child_id
   FROM TABLE1 WHERE col1=prev_id;
 END WHILE;
 select * from temp_table;
 END //

출력 결과를 저장하기 위해 임시 테이블을 사용하고 있으며 임시 테이블이 세션 기반이므로 출력 데이터가 잘못되었다는 문제가 발생하지 않을 것입니다.

SQL FIDDLE Demo


이 쿼리를 시도하십시오.

SELECT
    col1, col2, @pv := col3 as 'col3'
FROM
    table1
JOIN
    (SELECT @pv := 1) tmp
WHERE
    col1 = @pv

SQL FIDDLE Demo:

| COL1 | COL2 | COL3 |
+------+------+------+
|    1 |    a |    5 |
|    5 |    d |    3 |
|    3 |    k |    7 |

이 솔루션이 작동
parent_id하려면 참고 값이보다 작아야합니다 child_id.


답변

@Meherzad가 수락 한 답변은 데이터가 특정 순서 인 경우에만 작동합니다. OP 질문의 데이터로 작업합니다. 제 경우에는 데이터 작업을 위해 수정해야했습니다.

참고 이것은 모든 레코드의 “id”(질문의 col1) 값이 해당 레코드의 “parent id”(질문의 col3)보다 큰 경우에만 작동합니다. 일반적으로 부모를 먼저 만들어야하기 때문에 이러한 경우가 많습니다. 그러나 응용 프로그램이 계층 구조 변경을 허용하는 경우 항목이 다른 곳에서 다시 부모가 될 수있는 경우에는이를 신뢰할 수 없습니다.

누군가에게 도움이되는 경우를 대비 한 내 질문입니다. 데이터가 위에서 설명한 필수 구조를 따르지 않기 때문에 주어진 질문에서 작동하지 않습니다.

select t.col1, t.col2, @pv := t.col3 col3
from (select * from table1 order by col1 desc) t
join (select @pv := 1) tmp
where t.col1 = @pv

차이점은 부모가 그 뒤에 오도록 (부모의 가치가 자식의 가치보다 낮기 때문에) table1순서가 col1지정된다는 것 col1입니다.


답변

leftclickben 대답 이 나를 위해 일했지만 주어진 노드에서 트리를 백업하는 경로를 원했고 이러한 경로는 트리 아래로 다른 방향으로 진행되는 것처럼 보였습니다. 그래서 저는 일부 필드를 뒤집고 명확성을 위해 이름을 변경해야했습니다.이게 다른 사람이 원하는 경우를 대비하여 저에게 효과적입니다.

item | parent
-------------
1    | null
2    | 1
3    | 1
4    | 2
5    | 4
6    | 3

select t.item_id as item, @pv:=t.parent as parent
from (select * from item_tree order by item_id desc) t
join
(select @pv:=6)tmp
where t.item_id=@pv;

제공합니다 :

item | parent
-------------
6    | 3
3    | 1
1    | null


답변

저장 프로시 저는이를 수행하는 가장 좋은 방법입니다. Meherzad의 솔루션은 데이터가 동일한 순서를 따르는 경우에만 작동하기 때문입니다.

이와 같은 테이블 구조가 있다면

col1 | col2 | col3
-----+------+------
 3   | k    | 7
 5   | d    | 3
 1   | a    | 5
 6   | o    | 2
 2   | 0    | 8

작동하지 않습니다. SQL Fiddle Demo

다음은 동일한 작업을 수행하는 샘플 절차 코드입니다.

delimiter //
CREATE PROCEDURE chainReaction
(
    in inputNo int
)
BEGIN
    declare final_id int default NULL;
    SELECT col3
    INTO final_id
    FROM table1
    WHERE col1 = inputNo;
    IF( final_id is not null) THEN
        INSERT INTO results(SELECT col1, col2, col3 FROM table1 WHERE col1 = inputNo);
        CALL chainReaction(final_id);
    end if;
END//
delimiter ;

call chainReaction(1);
SELECT * FROM results;
DROP TABLE if exists results;


답변

상위 ID가 하위 ID보다 낮아야하는 문제없이 SELECT를 할 수 있도록하려면 함수를 사용할 수 있습니다. 또한 여러 자식을 지원하며 (트리가해야하는 것처럼) 트리는 여러 머리를 가질 수 있습니다. 또한 데이터에 루프가 있으면 중단됩니다.

동적 SQL을 사용하여 테이블 / 열 이름을 전달할 수 있기를 원했지만 MySQL의 함수는이를 지원하지 않습니다.

DELIMITER $$

CREATE FUNCTION `isSubElement`(pParentId INT, pId INT) RETURNS int(11)
DETERMINISTIC
READS SQL DATA
BEGIN
DECLARE isChild,curId,curParent,lastParent int;
SET isChild = 0;
SET curId = pId;
SET curParent = -1;
SET lastParent = -2;

WHILE lastParent <> curParent AND curParent <> 0 AND curId <> -1 AND curParent <> pId AND isChild = 0 DO
    SET lastParent = curParent;
    SELECT ParentId from `test` where id=curId limit 1 into curParent;

    IF curParent = pParentId THEN
        SET isChild = 1;
    END IF;
    SET curId = curParent;
END WHILE;

RETURN isChild;
END$$

여기에서 테이블 test을 실제 테이블 이름으로 수정해야하며 열 (ParentId, Id)을 실제 이름으로 조정해야 할 수 있습니다.

사용법 :

SET @wantedSubTreeId = 3;
SELECT * FROM test WHERE isSubElement(@wantedSubTreeId,id) = 1 OR ID = @wantedSubTreeId;

결과 :

3   7   k
5   3   d
9   3   f
1   5   a

테스트 생성을위한 SQL :

CREATE TABLE IF NOT EXISTS `test` (
  `Id` int(11) NOT NULL,
  `ParentId` int(11) DEFAULT NULL,
  `Name` varchar(300) NOT NULL,
  PRIMARY KEY (`Id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1;

insert into test (id, parentid, name) values(3,7,'k');
insert into test (id, parentid, name) values(5,3,'d');
insert into test (id, parentid, name) values(9,3,'f');
insert into test (id, parentid, name) values(1,5,'a');
insert into test (id, parentid, name) values(6,2,'o');
insert into test (id, parentid, name) values(2,8,'c');

편집 : 여기에 직접 테스트 하는 바이올린 이 있습니다. 사전 정의 된 구분 기호를 사용하여 구분자를 변경해야했지만 작동합니다.


답변

Master DJon에서 구축

다음은 깊이 반환의 추가 유틸리티를 제공하는 단순화 된 기능입니다 (로직을 사용하여 상위 작업을 포함하거나 특정 깊이에서 검색하려는 경우).

DELIMITER $$
FUNCTION `childDepth`(pParentId INT, pId INT) RETURNS int(11)
    READS SQL DATA
    DETERMINISTIC
BEGIN
DECLARE depth,curId int;
SET depth = 0;
SET curId = pId;

WHILE curId IS not null AND curId <> pParentId DO
    SELECT ParentId from test where id=curId limit 1 into curId;
    SET depth = depth + 1;
END WHILE;

IF curId IS NULL THEN
    set depth = -1;
END IF;

RETURN depth;
END$$

용법:

select * from test where childDepth(1, id) <> -1;


답변