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

Tum*_*Tum 79 mysql sql query-optimization recursive-query

我有一张桌子:

col1 | col2 | col3
-----+------+-------
1    | a    | 5
5    | d    | 3
3    | k    | 7
6    | o    | 2
2    | 0    | 8
Run Code Online (Sandbox Code Playgroud)

如果用户搜索"1",程序将查看col1具有"1"的程序,然后它将获得col3"5"中的值,然后程序将继续搜索"5",col1并且将获得"3" in col3等等.所以它会打印出来:

1   | a   | 5
5   | d   | 3
3   | k   | 7
Run Code Online (Sandbox Code Playgroud)

如果用户搜索"6",它将打印出来:

6   | o   | 2
2   | 0   | 8
Run Code Online (Sandbox Code Playgroud)

如何构建SELECT查询来做到这一点?

Meh*_*zad 69

编辑

@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 //
Run Code Online (Sandbox Code Playgroud)

我们使用临时表来存储输出的结果,并且因为临时表是基于会话的,所以我们不会有任何关于输出数据不正确的问题.

SQL FIDDLE Demo

试试这个查询:

SELECT 
    col1, col2, @pv := col3 as 'col3' 
FROM 
    table1
JOIN 
    (SELECT @pv := 1) tmp
WHERE 
    col1 = @pv
Run Code Online (Sandbox Code Playgroud)

SQL FIDDLE Demo:

| COL1 | COL2 | COL3 |
+------+------+------+
|    1 |    a |    5 |
|    5 |    d |    3 |
|    3 |    k |    7 |
Run Code Online (Sandbox Code Playgroud)

注意
parent_id值应小child_id于此解决方案的工作值.

  • 只需要小心他的解决方案,没有循环类型依赖,然后它将进入无限循环,还有一件事它只能找到那个`col3`类型的1条记录,所以如果有多条记录那么它将无法工作. (5认同)
  • 这不是解决方案.这只是表扫描的幸运副作用.仔细阅读@leftclickben的答案,或者你会像我一样浪费很多时间. (5认同)
  • People Pls将此答案标记为最佳解决方案,因为其他类似问题的解决方案(关于mysql中的Recursive Select)非常复杂,因为它需要创建表并将数据插入其中.这个解决方案非常优雅. (2认同)
  • @HamidSarfraz现在可以运行http://sqlfiddle.com/#!2/74f457/14.这对你有用.由于它用于顺序搜索,并且id将始终具有比父级更大的值,因为父级需要首先创建.如果您需要任何额外的细节,请告知Pl. (2认同)
  • 我知道递归SQL是如何工作的.MySQL没有实现递归CTE,因此一个可行的选项是你给出的链接(使用存储过程/函数).另一个是使用mysql变量.然而,这里的答案并不优雅,但相反,只是可怕.它没有显示递归SQL.如果它适用于您的情况,那只是偶然的,正如@jaehung正确指出的那样.我不介意可怕的答案.我只是低估了他们.但是在50岁时我会回答一个可怕的答案. (2认同)

lef*_*ben 51

@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
Run Code Online (Sandbox Code Playgroud)

不同之处在于table1订购方式,col1以便父母将在其之后(因为父母的col1价值低于孩子的价值).


BoB*_*B3K 18

leftclickben的回答对我有用,但是我想要一个从给定节点回溯树到根的路径,这些似乎是从另一个方向走到树下.因此,为了清晰起见,我不得不翻转一些字段并重新命名,这对我有用,以防万一其他人也想要这样做 -

item | parent
-------------
1    | null
2    | 1
3    | 1
4    | 2
5    | 4
6    | 3
Run Code Online (Sandbox Code Playgroud)

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;
Run Code Online (Sandbox Code Playgroud)

得到:

item | parent
-------------
6    | 3
3    | 1
1    | null
Run Code Online (Sandbox Code Playgroud)


Jaz*_*min 7

存储过程是最好的方法.因为Meherzad的解决方案只有在数据遵循相同的顺序时才有效.

如果我们有这样的表结构

col1 | col2 | col3
-----+------+------
 3   | k    | 7
 5   | d    | 3
 1   | a    | 5
 6   | o    | 2
 2   | 0    | 8
Run Code Online (Sandbox Code Playgroud)

它不会工作. 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;
Run Code Online (Sandbox Code Playgroud)


Mas*_*Jon 7

如果您希望能够在没有父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$$
Run Code Online (Sandbox Code Playgroud)

在这里,必须将表test修改为实际表名,并且可能必须根据您的真实姓名调整列(ParentId,Id).

用法:

SET @wantedSubTreeId = 3;
SELECT * FROM test WHERE isSubElement(@wantedSubTreeId,id) = 1 OR ID = @wantedSubTreeId;
Run Code Online (Sandbox Code Playgroud)

结果:

3   7   k
5   3   d
9   3   f
1   5   a
Run Code Online (Sandbox Code Playgroud)

用于测试创建的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');
Run Code Online (Sandbox Code Playgroud)

编辑:这是一个自己测试它的小提琴.它强迫我使用预定义的分隔符更改分隔符,但它可以工作.