it-swarm.com.de

Wie kann ich die rekursive SELECT-Abfrage in MySQL ausführen?

Ich habe eine folgende Tabelle:

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

Wenn ein Benutzer nach "1" sucht, sucht das Programm nach col1, das "1" hat, und es wird ein Wert in col3 "5" angezeigt. Dann sucht das Programm weiter nach "5" in col1 und es wird angezeigt "3" in col3 und so weiter. So wird es ausgedruckt:

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

Wenn ein Benutzer nach "6" sucht, wird Folgendes ausgegeben:

6   | o   | 2
2   | 0   | 8

Wie erstelle ich eine SELECT-Abfrage?

77
Tum

Bearbeiten

Die von @leftclickben erwähnte Lösung ist auch effektiv. Wir können auch eine gespeicherte Prozedur dafür verwenden.

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

Wir verwenden die temporäre Tabelle, um die Ergebnisse der Ausgabe zu speichern, und da die temporären Tabellen sitzungsbasiert sind, wird es keine Probleme mit fehlerhaften Ausgabedaten geben.

SQL FIDDLE Demo

Versuchen Sie diese Abfrage:

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 |

Hinweis
parent_id-Wert sollte kleiner als der child_id sein, damit diese Lösung funktioniert.

67
Meherzad

Die akzeptierte Antwort von @Meherzad funktioniert nur, wenn sich die Daten in einer bestimmten Reihenfolge befinden. Es kommt vor, dass mit den Daten aus der OP-Frage gearbeitet wird. In meinem Fall musste ich sie ändern, um mit meinen Daten arbeiten zu können.

Hinweis Dies funktioniert nur, wenn die "id" jedes Datensatzes (col1 in der Frage) einen Wert hat, der größer ist als die "übergeordnete ID" dieses Datensatzes (col3 in der Frage). Dies ist häufig der Fall, da normalerweise das übergeordnete Element zuerst erstellt werden muss. Wenn Ihre Anwendung jedoch Änderungen an der Hierarchie zulässt, bei denen ein Element an anderer Stelle erneut übergeordnet werden kann, können Sie sich darauf nicht verlassen.

Dies ist meine Frage, falls es jemandem hilft; Beachten Sie, dass die angegebene Frage nicht funktioniert, da die Daten nicht der oben beschriebenen erforderlichen Struktur entsprechen.

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

Der Unterschied besteht darin, dass table1 von col1 geordnet wird, so dass das übergeordnete Element danach ist (da der col1-Wert des übergeordneten Elements niedriger ist als der des untergeordneten).

50
leftclickben

die Antwort von leftclickben funktionierte für mich, aber ich wollte einen Pfad von einem bestimmten Knoten in den Baum hinauf zur Wurzel, und dieser schien den anderen Weg zu gehen, den Baum hinunter. Also musste ich einige Felder umdrehen und aus Gründen der Übersichtlichkeit umbenennen, und das funktioniert für mich, falls dies auch jemand anderes wünscht -

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

und

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 [email protected];

gibt:

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

Gespeicherte Prozeduren sind der beste Weg, dies zu tun. Denn die Lösung von Meherzad würde nur funktionieren, wenn die Daten der gleichen Reihenfolge folgen.

Wenn wir eine solche Tabellenstruktur haben

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

Es wird nicht funktionieren. SQL Fiddle Demo

Hier ist ein Beispiel für einen Verfahrenscode, um dies zu erreichen.

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

Wenn Sie ein SELECT haben möchten, ohne dass Probleme der übergeordneten ID niedriger als die untergeordnete ID sind, kann eine Funktion verwendet werden. Es unterstützt auch mehrere Kinder (wie es ein Baum tun sollte) und der Baum kann mehrere Köpfe haben. Es stellt auch sicher, dass die Daten unterbrochen werden, wenn in den Daten eine Schleife vorhanden ist.

Ich wollte dynamisches SQL verwenden, um die Tabellen-/Spaltennamen übergeben zu können, aber Funktionen in MySQL unterstützen dies nicht.

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$$

Hier muss die Tabelle test an den tatsächlichen Tabellennamen angepasst werden und die Spalten (ParentId, Id) müssen möglicherweise an Ihre tatsächlichen Namen angepasst werden.

Verwendungszweck :

SET @wantedSubTreeId = 3;
SELECT * FROM test WHERE isSubElement(@wantedSubTreeId,id) = 1 OR ID = @wantedSubTreeId;

Ergebnis:

3   7   k
5   3   d
9   3   f
1   5   a

SQL zur Testerstellung:

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

EDIT: Hier ist eine Geige , um es selbst zu testen. Es zwang mich, das Trennzeichen mit dem vordefinierten zu ändern, aber es funktioniert.

6
Master DJon

Aufbauend auf Master DJon

Hier ist eine vereinfachte Funktion, die den zusätzlichen Nutzen für die Rückgabe von Tiefe bietet (falls Sie die übergeordnete Aufgabe oder die Suche mit einer bestimmten Tiefe logisch verwenden möchten).

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$$

Verwendungszweck:

select * from test where childDepth(1, id) <> -1;
0
patrick