如何在MySQL中进行递归选择查询?

如何在MySQL中进行递归选择查询?,mysql,sql,query-optimization,recursive-query,Mysql,Sql,Query Optimization,Recursive Query,我得到了下表: col1 | col2 | col3 -----+------+------- 1 | a | 5 5 | d | 3 3 | k | 7 6 | o | 2 2 | 0 | 8 如果用户搜索“1”,程序将查看具有“1”的col1,然后在col3“5”中获取一个值,然后程序将继续在col1中搜索“5”,并在col3中获取“3”,依此类推。所以它会打印出来: 1 | a | 5 5 | d | 3 3

我得到了下表:

col1 | col2 | col3
-----+------+-------
1    | a    | 5
5    | d    | 3
3    | k    | 7
6    | o    | 2
2    | 0    | 8
如果用户搜索“1”,程序将查看具有“1”的
col1
,然后在
col3
“5”中获取一个值,然后程序将继续在
col1
中搜索“5”,并在
col3
中获取“3”,依此类推。所以它会打印出来:

1   | a   | 5
5   | d   | 3
3   | k   | 7
6   | o   | 2
2   | 0   | 8
如果用户搜索“6”,它将打印出:

1   | a   | 5
5   | d   | 3
3   | k   | 7
6   | o   | 2
2   | 0   | 8
如何构建一个
选择
查询来实现这一点?

编辑

@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 //
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;

注意
parent\u id
值应小于
child\u id
,此解决方案才能工作

编辑

@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 //
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;

注意
parent\u id
值应小于
child\u id
,此解决方案才能工作


存储过程是最好的方法。因为只有当数据遵循相同的顺序时,Meherzad的解决方案才会起作用

如果我们有这样一个表结构

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

下面是一个实现相同功能的示例程序代码

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

存储过程是最好的方法。因为只有当数据遵循相同的顺序时,Meherzad的解决方案才会起作用

如果我们有这样一个表结构

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

下面是一个实现相同功能的示例程序代码

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

@Meherzad接受的答案仅在数据按特定顺序排列时有效。它恰好与OP问题中的数据一起工作。在我的例子中,我不得不修改它来处理我的数据

注意只有当每条记录的“id”(问题中的col1)的值大于该记录的“父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
值低于子对象的值)。

只有当数据按特定顺序排列时,@Meherzad接受的答案才有效。它恰好与OP问题中的数据一起工作。在我的例子中,我不得不修改它来处理我的数据

注意只有当每条记录的“id”(问题中的col1)的值大于该记录的“父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
值低于子节点的值)。

对我来说很有效,但我想要一条从给定节点到树上根节点的路径,而这些路径似乎是从树的另一个方向返回的。因此,为了清晰起见,我必须翻转一些字段并重命名,这对我来说很有效,以防其他人也希望如此--

给出:

item | parent
-------------
6    | 3
3    | 1
1    | null
对我来说是可行的,但我想要一条从给定节点到树根的路径,而这些路径似乎是朝着另一个方向,沿着树向下。因此,为了清晰起见,我必须翻转一些字段并重命名,这对我来说很有效,以防其他人也希望如此--

给出:

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

如果您希望能够拥有SELECT,而不存在父id必须低于子id的问题,则可以使用函数。它还支持多个子树(树应该这样做),树可以有多个头。它还确保在数据中存在循环时中断

我想使用动态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$$
结果:

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

编辑:这里有一个测试自己。它迫使我使用预定义的分隔符来更改分隔符,但它可以工作。

如果您希望能够进行选择,而不会出现父id必须低于子id的问题,则可以使用函数。它还支持多个子树(树应该这样做),树可以有多个头。它还确保在数据中存在循环时中断

我想使用动态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$$
结果:

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构建的

这里是一个简化的函数,它提供了返回深度的附加实用程序(如果您想使用逻辑包含父任务或特定深度的搜索)

分隔符$$
函数'childDepth'(pParentId INT,pId INT)返回INT(11)
读取SQL数据
确定性
开始
声明深度,curId int;
设置深度=0;
设置curId=pId;
而curId不为null,curId pParentId为
从测试中选择ParentId,其中id=curId limit 1进入curId;
设置深度=深度+1;
结束时;
如果curId为NULL,那么
设置深度=-1;
如果结束;
返回深度;
结束$$
用法:

select * from test where childDepth(1, id) <> -1;
从测试中选择*,其中childDepth(1,id)-1;
主DJo的建筑