[SQL(Firebird)]: Rekursion und Baumstruktur Problem

Onkel Homie

Gesperrt
Mitglied seit
26.03.2003
Beiträge
15.842
Renomée
390
Standort
Dortmund
  • SIMAP Race
  • QMC Race
  • Spinhenge ESL
Mahlzeit zusammen,

ich stehe gerade vor einem, für mich recht kniffligen, SQL Problem. Ich muss dazu sagen das ich bisher kaum was mit (Firebird)SQL gemacht habe und dort auch erst recht nichts mit Rekursionen, also bitte bei Anfängerfehlern nachsichtig sein ;)

Vielleicht kann mir ja jemand helfen.

Nun aber zu meinem Problem:

Ausgangssituation:
2 Tabellen (T1, T2) sollen einen Baum ergeben dessen Struktur beispielsweise wie folgt aussieht:

Code:
Element aus T1
|
| - Element aus T2
|   |
|   |- Element aus T2
|   |
|   |- Element aus T2
|      |
|      |-Element aus T2
|
| - Element aus T2
...

Wichtig hierbei ist, dass ein Element aus T1 niemals das Child eines Elements aus T2 seien kann und es auch immer nur ein Element aus T1 im Baum als Root gibt.

Der problematische Teil stellt nun also die Anordnung der Elemente aus T2 dar.

Ich suche jetzt nach Möglichkeit eine rekursive SQL Anweisung um die korrekte Baumstruktur zu bekommen.

Einzelne Elemente sehen dabei wie folgt aus (E1 = Element aus T1, E2 = Element aus T2):

E1.ID
E2.ID, E2.RefUebergeordneteID (kann sowohl eine E1.ID als auch eine E2.ID sein), E2.RefUebergeordneteE1 (wurde bereits von mir hinzugefügt um etwas sortieren zu können)

Die anderen Attribute sind hier unwichtig, es geht nur um die Zuordnung der IDs bzw. eben der referenzierten IDs.

Das Problem ist, dass die ID jeweils GUI-IDs sind und keine aufeinander folgenden Indizes. Diese Struktur kann ich auch nicht groß umstellen. Insgesamt habe ich leider wenig Möglichkeiten die bereits vorhandene Struktur zu erweitern, ließe sich aber ggf. drüber diskutieren ;).

Mein Ansatz sieht nun bisher wie folgt aus:

Code:
WITH  RECURSIVE rek (ID, RefUebergeordneteID, RefUebergeordneteE1)
AS (
    SELECT ID, RefUebergeordneteID, RefUebergeordneteE1
    FROM T2
    Where RefUebergeordneteID =  ' <eine bestimmte GUI-ID>'
    UNION ALL
    SELECT child.ID, child.RefUebergeordneteID, child.RefUebergeordneteE1
    FROM rek parent,
        T2 child
    WHERE parent.ID = child.RefUebergeordneteID
)
SELECT DISTINCT ID, RefUebergeordneteID, RefUebergeordneteE1
FROM rek

Was dann leider zu einem durcheinander gewürfelten Ergebnis führt, da immer automatisch nach der kleinsten GUI-ID sortiert wird. Rekursionen schachteln geht scheinbar nicht in SQl (? meines Wissens sind nur From, Select und Where schachtelbar), daher komme ich nicht wirklich weiter. Denn das aktuell Ergebnis kriege ich auch viel einfacher mit einem:

Code:
Select ID, RefUebergeordneteID, RefUebergeordneteE1
From T2 
Where RefUebergeordneteE1 = ' <eine bestimmte GUI-ID>'

An sich dachte ich eben die Rekursion würde meine Elemente nach folgendem Schema ausgeben:

- hole alle T2.RefUebergeordneteID mit ' <eine bestimmte GUI-ID>' als Tabelle rek
- hole alle Childs mit T2.RefUebergeordneteID = rek.ID wiederum als rek usw.

Nur leider wird dann halt sortiert *noahnung*

Wichtig ist auch noch das eine nested Struktur nicht in Frage kommt, da ständig Daten in die Tabelle 2 der Datenbank an beliebigen Stellen geschrieben, kopiert oder verschoben werden können und dann irgendwann der Aufwand zu groß wird die ganze Struktur immer neu durchzurechnen.

Falls es noch wichtig bzw. von Interesse ist: Es geht darum mittels eines C# Programms halt auf eine Firebird DB zuzugreifen und sich dabei viele Selectes zu ersparen, da aktuell die Situation so ist das bestimmte große vom Programm gestellte Anfragen alles lahmlegen ;D Denn aktuell laufen da viele while-Schleifen ineinander die alle fröglich ihre Selects abfeuern, was einerseits lange dauert, andererseits halt den Speicher doch recht extrem belastet. Die Rede ist hier von locker 15.000 - 20.000 Anfragen die da beim Laden rausgehen. Das habe ich nicht gemacht, dass war netterweise schon so und lief bisher auch ganz gut...solange die Projekte an sich klein genug waren ;D Daher ist das problem wohl bisher nie aufgefallen. Nur jetzt muss der Firebird-Server (die Software nicht die Hardware wo die DBs liegen) plötzlich um die 1,8GB bewältigen, was in der Version 2.1 auch nur unter Linux geht. Unter Windows steigt er einfach aus, der 2.5er Beta läuft durch aber es dauert immernoch sau lange die Daten zu laden so ca. 7-10min. Und in absehbarer Zeit ist die 6-7 fache Datenmange zu erwarten hehe.

Wäre klasse wenn jemand vielleicht einen Denkanstoß bzw. einen Lösungsansatz hätte :)
.
EDIT :
.

Einen Schritt weiter aber leider noch nicht ganz fertig sieht das Ganze nun so aus:
Code:
WITH  RECURSIVE rek (ID, RefUebergeordneteID, RefUebergeordneteE1, stufe)
AS (
    SELECT ID, RefUebergeordneteID, RefUebergeordneteE1, 1 as stufe
    FROM T2
    Where RefUebergeordneteID =  ' <eine bestimmte GUI-ID>'
    UNION ALL
    SELECT child.ID, child.RefUebergeordneteID, child.RefUebergeordneteE1, parent.stufe+1
    FROM rek parent,
        T2 child
    WHERE parent.ID = child.RefUebergeordneteID
)
SELECT DISTINCT ID, RefUebergeordneteID, RefUebergeordneteE1, stufe
FROM rek
Order by stufe

Das Ergebnis ist schon mal besser, aber der "counter" mit der stufe zählt noch nicht richtig hoch. An sich müsste er an eienr anderen Stelle sitzen, aber da kann ich ihn nirgends reinsetzen *noahnung*.

Im Prinzip würde mir ein group by RefUebergeordneteID vor dem Order by womöglich helfen, aber das kann wohl auf Grund der Rekursion nicht angewendet werden.

So langsam glaube ich das ich mein Problem mit reinem SQL nicht lösen kann und die Anwendung da selber doch mal sortieren muss. Dann muss ich zwar wieder ein paar Sachen nachladen, aber ich kann noch irgendwie versuchen die monströse Anzahl der Selects zu reduzieren.

Aber vielleicht hat hier ja doch noch wer die rettende Idee :)
 
Zuletzt bearbeitet:
Theoretisch müsste es funktionieren, wenn Du eine Stored-Procedure benutzt, welche sich selbst rekursiv aufruft - ich kenne mich aber mit Firebird zu wenig aus um das zu garantieren.
 
Stores Procedures kommen an sich wohl auch nicht so in Frage...so sagte man mir. Bin halt noch rehct neu an der Sache dran, aber man muss im Hinterkopd behalten das die Software häufiger upgedatet wird und eben jene Updates möglichst nicht die Datenbank betreffen sollten bzw. wenn dort was nötig ist, sollte dies möglichst einfach zu handeln sein. Kann sonst nämlich fiese Probleme bei den Kunden geben. (genau aus dem Grund hänge ich gerade an dem Kram *g*).

Ich bastel gearde schon an einer möglichen anderen Lösung.

Aber danke für den Vorschlag.

Firebird ist übrigens an sich schon recht striktes SQL, oder sind etwas anders umgesetzt, wie z.B. hier auch die Rekusrion. Im Netz findet man zumindest bei den Standard Beispielen nur WITH und nicht WITH RECURSIVE wie es bei Firebird seien muss.
 
Zurück
Oben Unten