簡體   English   中英

如何在 MySQL 中進行遞歸 SELECT 查詢?

[英]How to do the Recursive SELECT query in MySQL?

我得到了下表:

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”,它會打印出:

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)的值大於該記錄的“父 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

不同之處在於table1col1排序,因此父級將在它之后(因為父級的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');

編輯:這是一個可以自己測試的小提琴 它迫使我使用預定義的分隔符更改分隔符,但它有效。

建立在 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;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM