Ich habe 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
"1", dann erhält es einen Wert in col3
"5", dann sucht das Programm weiter nach "5" in col1
und erhält "3". in col3
und so weiter. Also wird es ausgedruckt:
1 | a | 5
5 | d | 3
3 | k | 7
Wenn ein Benutzer nach "6" sucht, wird Folgendes ausgedruckt:
6 | o | 2
2 | 0 | 8
Wie erstelle SELECT
ich eine Abfrage, um das zu tun?
Antworten:
Bearbeiten
Die von @leftclickben erwähnte Lösung ist ebenfalls 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 temporäre Tabellen, um die Ergebnisse der Ausgabe zu speichern, und da die temporären Tabellen sitzungsbasiert sind, gibt es keine Probleme mit falschen Ausgabedaten.
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 |
quelle
col3
Typs gefunden. Wenn es also mehrere Datensätze gibt, funktioniert es nicht.Die von @Meherzad akzeptierte Antwort funktioniert nur, wenn die Daten in einer bestimmten Reihenfolge vorliegen. Es funktioniert zufällig mit den Daten aus der OP-Frage. In meinem Fall musste ich es ändern, um mit meinen Daten zu arbeiten.
Hinweis Dies funktioniert nur, wenn die "ID" jedes Datensatzes (Spalte 1 in der Frage) einen Wert hat, der größer ist als die "Eltern-ID" des Datensatzes (Spalte 3 in der Frage). Dies ist häufig der Fall, da normalerweise zuerst das übergeordnete Element erstellt werden muss. Wenn Ihre Anwendung jedoch Änderungen an der Hierarchie zulässt, bei denen ein Element möglicherweise an einer anderen Stelle erneut übergeordnet wird, können Sie sich nicht darauf verlassen.
Dies ist meine Frage, falls es jemandem hilft; Beachten Sie, dass es mit der angegebenen 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
nach geordnet wird,col1
damit der Elternteil hinter ihm her ist (da dercol1
Wert des Elternteils niedriger ist als der des Kindes).quelle
Die Antwort von leftclickben funktionierte für mich, aber ich wollte einen Pfad von einem bestimmten Knoten zurück zum Baum zur Wurzel, und diese schienen in die andere Richtung zu gehen, den Baum hinunter. Also musste ich einige Felder umdrehen und aus Gründen der Klarheit umbenennen, und das funktioniert für mich, falls dies auch jemand anderes möchte -
item | parent ------------- 1 | null 2 | 1 3 | 1 4 | 2 5 | 4 6 | 3
und
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;
gibt:
item | parent ------------- 6 | 3 3 | 1 1 | null
quelle
1 3 6
Ich benutzearray_reverse()
stattdessen in PHP ..... eine SQL-Lösung dafür?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 Prozedurcode, um dasselbe 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;
quelle
Wenn Sie eine SELECT-Funktion haben möchten, ohne dass die übergeordnete ID niedriger als die untergeordnete ID sein muss, 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 wird auch sichergestellt, dass eine Unterbrechung auftritt, wenn eine Schleife in den Daten 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 realen Tabellennamen angepasst werden und die Spalten (ParentId, Id) müssen möglicherweise an Ihre realen Namen angepasst werden.Verwendung :
SET @wantedSubTreeId = 3; SELECT * FROM test WHERE isSubElement(@wantedSubTreeId,id) = 1 OR ID = @wantedSubTreeId;
Ergebnis:
SQL für die 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 hat mich gezwungen, das Trennzeichen mit dem vordefinierten zu ändern, aber es funktioniert.
quelle
Aufbauend auf Master DJon
Hier ist eine vereinfachte Funktion, die den zusätzlichen Nutzen der Rückgabe von Tiefe bietet (falls Sie die Logik verwenden möchten, um die übergeordnete Aufgabe einzuschließen oder in einer bestimmten Tiefe zu suchen).
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$$
Verwendung:
select * from test where childDepth(1, id) <> -1;
quelle