如何做MySQL中的recursionSELECT查询?

我有一张表格:

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

如果用户search“1”,程序将查看具有“1”的col1 ,然后在col3 “5”中得到一个值,然后程序将继续在col1search“5”,它将得到col3 “3”,依此类推。 所以它会打印出来:

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

如果用户search“6”,则会打印出:

 6 | o | 2 2 | 0 | 8 

如何build立一个SELECT查询来做到这一点?

编辑

@leftclickben提到的解决scheme也是有效的。 我们也可以使用相同的存储过程。

 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值应该小于此解决scheme的child_id工作。

@Meherzad接受的答案只有在数据按特定顺序时才有效。 它正好与OP问题的数据一起工作。 就我而言,我不得不修改它来处理我的数据。

注意这只适用于每个logging的“id”(col1在问题中)的值大于该logging的“父id”(col3在问题中)。 这通常是这种情况,因为通常需要首先创build父项。 但是,如果您的应用程序允许更改层次结构,其中某个项目可能会重新在其他地方重新显示,那么您不能依赖此项。

这是我的查询,如果它帮助某人; 注意它不适用于给定的问题,因为数据不符合上述要求的结构。

 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是按col1sorting的,因此父项会在后面(因为父项的col1值低于子项)。

leftclickben的答案为我工作,但我想要从一个给定节点的path支持树到根,这些似乎是另一种方式,在树下。 所以,我不得不翻转一些字段,为了清晰起见而重新命名,这对我来说很有用,万一别人也想这样做 –

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

 select t.item_id as item_id, @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的解决scheme才能工作。

如果我们有这样的表结构

 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,可以使用一个函数。 它也支持多个孩子(像树一样),树可以有多个头。 它还可以确保在数据中存在循环时中断。

我想使用dynamicSQL能够传递表/列的名称,但在MySQL中的function不支持这一点。

 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 

用于testing创build的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'); 

编辑:这是一个小提琴来testing它自己。 它迫使我改变使用预定义的分隔符,但它的工作原理。