MySQL-Abfrage mit "Rekursion" möglich?
Daniel
- datenbank
Moin!
Vielleicht kann mir hier jemand eine Antwort geben.
Folgendes Problem: Ich habe hier eine Datenbank wo in einer Tabelle folgenden Daten drin sind: ID, Name, Parent-ID.
Die Parent-ID verweist auf die jeweilige Eltern-ID in derselben Tabelle. Eine Abfrage soll jetzt alle Datensätze die mit einer bestimmten Eltern-ID verknüpft sind auflisten z.B. alle Datensätze die 1 als Parent-ID haben.
Jetzt kann das Ganze aber theoretisch unbegrenzt in die Tiefe gehen z.B. 6 hat Parent-ID 4, 4 hat Parent-ID 2 und 2 hat erst Parent-ID 1. Nun sollen aber auch die Datensätze mit ID 6 und 4 berücksichtigt werden und nicht nur der Datensatz mit ID 2.
Es müsste also eine Art Rekursion stattfinden. Ist so etwas mit MySQL überhaupt möglich oder kann ich das nur umsetzen mit Hilfe eines z.B. PHP-Skripts? Ich brauche so ein Anfrage für eine (wahrscheinlich) einmalige interne Sache, Performance ist also ziemlich egal.
Und bitte keine Antworten wie "Notfalls von Hand in die Einträge schauen". Die Datenbank hat mehrere 10.000 Einträge, da werde ich mit Sicherheit nicht alles einzeln durchforsten.
Gruß
Daniel
mit JSP könntest du es folgendermaßen machen:
aber ich würde mir das genau überlegen, wenn du irgendwo einen falschen Eintrag hast, kommst du schnell in eine Endlosschleife
mit den heutigen möglichkeiten liegt das bie mysql nicht drin.
man kann aber die tabelle mit sich selbst 'join'en.
z.b.:
SELECT E.ElternID, P.ParentID
FROM tabelle E join tabelle P
ON E.ElternID = P.ParentID
mit den heutigen möglichkeiten liegt das bie mysql nicht drin.
Danke für die Antwort. Dann muss ich mir ein Skript basteln womit ich das mache. Habe eigentlich schon geahnt, dass das mit MySQL nicht drin ist. Wollte nur zur Sicherheit noch andere Meinungen dazu hören.
Gruß
Daniel
Hello,
Danke für die Antwort. Dann muss ich mir ein Skript basteln womit ich das mache. Habe eigentlich schon geahnt, dass das mit MySQL nicht drin ist. Wollte nur zur Sicherheit noch andere Meinungen dazu hören.
Da füllst Du dann einfach ein Mehrdimensionales Array mit dem Level als Index. Das übergibst du der Funktion einfach immer wieder. Vergiss nicht einen Rekursionszähler ins Array einzubauen.
Harzliche Grüße aus http://www.annerschbarrich.de
Tom
Hallo Daniel,
Jetzt kann das Ganze aber theoretisch unbegrenzt in die Tiefe gehen z.B. 6 hat Parent-ID 4, 4 hat Parent-ID 2 und 2 hat erst Parent-ID 1. Nun sollen aber auch die Datensätze mit ID 6 und 4 berücksichtigt werden und nicht nur der Datensatz mit ID 2.
Das ist schlecht, aber einfach zu realisieren :-)
5 hat Parent-ID 3,
3 hat Parent-ID 5
Es müsste also eine Art Rekursion stattfinden. Ist so etwas mit MySQL überhaupt möglich oder kann ich das nur umsetzen mit Hilfe eines z.B. PHP-Skripts? Ich brauche so ein Anfrage für eine (wahrscheinlich) einmalige interne Sache, Performance ist also ziemlich egal.
MySQL unterstützt keine Stored Procedures, also kannst Du das nicht in MySQL lösen. Du solltest es skriptgesteuert tun. Dabei unbedingt auf Vorkommen von Schleifen überprüfen.
Freundliche Grüsse,
Vinzenz
Hello,
5 hat Parent-ID 3,
3 hat Parent-ID 5
und Tschüss
5 hat Parent-ID 3, Level 4
3 hat Parent-ID 5, Level 3
Wenn jetzt wieder 5 geholt wird, müsste der Level==2 lauten und nicht 4 und es gibt einen ErrorLog, jedenfalls bei mir... ;-)
Harzliche Grüße aus http://www.annerschbarrich.de
Tom
Hello,
ich habe die Frage auch mal gehabt. Leider geht es mit MySQL nicht in einem Statement. Der vorgeschlagene Self-Join hilf auch nur eine Stufe weiter.
Ich habe also die Rekursion in meine API (PHP) gelegt. Zur Sicherheit, dass sich das System nicht bei fehlerhaften Daten totläuft, gibt es auch noch ein Level-Feld, dass die jeweilige Hierarchiestufe speichert. Wenn der Parent dann nich einen Level kleiner (=höher) hat, als das Child, dann liegt ein Fehler vor, und die Schleife bricht ab.
Außerdem gibt es bei mir noch eine Konstante DB_MAX_RECURSION
Außerdem kann man auf diese Weise auch verschiedene Familien in einer Tabelle speichern.
Die Abfragen gehen eigentlich auch ganz schnell. Die mesiten beginnen derzeit auf Level 4 und müssen also vier Elternstufen liefern. Von der Last merke ich da noch nichts.
Harzliche Grüße aus http://www.annerschbarrich.de
Tom
ich habe die Frage auch mal gehabt. Leider geht es mit MySQL nicht in einem Statement. Der vorgeschlagene Self-Join hilf auch nur eine Stufe weiter.
dies sollte keine lösung sein, sondern ein gedanklicher ansatz.
man kann die join's natürlich auch noch weiter schachteln, und so eine größere tiefe erreichen, wobei in jeder schachtelung auch eine zusätzliche spalte z.b. schachtelung=3 generiert werden kann.
um hierbei die tiefe zu kontrollieren, muß in der tabelle natürlich eine ebene enthalten sein.
diese ebene kann nun auch in der on bedingung angewandt werden:
on E.ebene < P.ebene
yo,
ich würde dir raten, rekursionen aus dem datenbank-design rauszunehmen. in der praxis wird es sicherlich oft benutzt, aber es sollte vermieden werden. rekursionen führen zu einigen problemen. eine zweite tabelle ist hier anzuraten, wo die id und die parent_id stehen.
Ilja
Hello,
ich würde dir raten, rekursionen aus dem datenbank-design rauszunehmen. in der praxis wird es sicherlich oft benutzt, aber es sollte vermieden werden. rekursionen führen zu einigen problemen. eine zweite tabelle ist hier anzuraten, wo die id und die parent_id stehen.
Und damit Du die dann nicht rekursiv abfragen musst, baust Du am besten eine dritte Tabelle, und damit Du die dann nicht rekursiv abfragen musst, baust Du am besten eine vierte Tabelle, und damit Du die dann nicht rekursiv abfragen musst, baust Du am besten eine fünfte Tabelle, und damit Du die dann nicht rekursiv abfragen musst, baust Du am besten eine sechste Tabelle, und damit Du die dann nicht rekursiv abfragen musst, baust Du am besten eine siebte Tabelle.
Für den Anfang reichen sieben Ebenen. Und nun weißt Du ja, wie man[tm] es machen soll.
Harzliche Grüße aus http://www.annerschbarrich.de
Tom
yp,
wie gesagt, ich würde eine zusätzliche tabelle empfehlen, nicht mehrere.
Ilja
Hello,
Wenn die Anzahl deiner Sätze pro Ebene begrenzbar ist, kommst Du sogar vollständig ohne Rekursion aus: mit gezielter Denormalisierung.
Schlüsselfeld sehr lang
0001.
0001.0001.
0001.0002.
0001.0001.0001
0001.0001.0001.0001
0002.
0002.0001.0001.
0002.0001.0002.
0002.0001.0003.
0002.0002.0001.
0002.0002.0002.
usw.
Ich denke, du siehst, wie die Begrenzug der Nummernkreise pro Ebene die Rekursion vermeiden hilft.
Es lassen sich so sofort alle Eltern und Kinder _in_einem_Statement_ besorgen.
Angenommen, Du hast das Child
0001.0001.0001.0001
und möchtest nun den kürzesten Weg zur Wurzel zurückverfolgen, dann muss der Wert in der Datenbank einfach immer mit dem entprechenden linken Teil der Child-ID übereinstimmen.
Pseudeocode:
select ID from $table where ID=left($child(length(trim((ID)));
oder:
select ID from $table where locate(trim(ID),$child)=1;
Harzliche Grüße aus http://www.annerschbarrich.de
Tom