如何做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”中得到一个值,然后程序将继续在col1
search“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
是按col1
sorting的,因此父项会在后面(因为父项的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它自己。 它迫使我改变使用预定义的分隔符,但它的工作原理。