![]() |
Baumstruktur selber zeichnen
Hallo,
Ich hab in einer Datenbank Datensätze, die untereinander wie in einer Baumstruktur verknüpft sind Jeder Datensatz hat eine Eindeutige Nummer --> Feld (INR) und eine Verknüpfung zur INR eines anderen (vorgelagerten) Datensatzes --> Feld (PR) Root-Datensätze haben als PR = 1 Es gibt maximal 10 Ebenen: ich könnte die ganze Datenbank durchgehen mit: Query_1 select * from DB where PR = 1 (Root) Query_1 onDataChange = while not query_1.EOF -> Query_2 select * from DB where PR = INR von Query_1 Query_2 onDataChange = while not query_2.EOF -> Query_3 select * from DB where PR = INR von Query_2 Query_3 onDataChange = while not query_3.EOF -> Query_4 select * from DB where PR = INR von Query_3 Query_4 onDataChange = while not query_4.EOF -> Query_5 select * from DB where PR = INR von Query_4 Query_5 onDataChange = while not query_5.EOF -> Query_6 select * from DB where PR = INR von Query_5 ... Ich weiß, wie viele UnterObjekte ein Objekt besitzt (recordcount..) Ich weiß, wie groß ein Datensatz dargestellt werden soll (panel mit 130px mal 25px inkl. Beschriftung) mit OnClick -> eindeutige INR des Datensatzes wird an procedure gesendet Ich weiß aber NICHT, in welcher Ebene der Datensatz ist - ich hab kein Datenbankfeld, wo das ersichtlich wäre (hat sich bis dato immer als Knackpunkt erwiesen) Jetzt will ich die Datensätz (als Panel) wie in einer Baumstruktur zeichnen... :pale: Im Vorfeld dachte ich mir, das wird ganz einfach zu lösen sein .. (naiv) Die Probleme haben aber überhand genommen ;-) Gibt es dafür einen fertigen Algorithmus oder was brauchbares, der mir die Position zum Zeichnen berechnet? Vielen Dank für Tipps und Hinweise - wie man sowas richtig macht :stupid: Erich |
AW: Baumstruktur selber zeichnen
Schau doch mal bei der Rachel vorbei. Ist zwar nicht Delphi aber recht gut beschrieben, was sie da macht.
![]() |
AW: Baumstruktur selber zeichnen
Hallo,
ist zwar nicht optimal aber als Grundstruktur sollte es so gehen...
Delphi-Quellcode:
procedure printTree(const aPR, alevel:integer);
var aQuery:TQuery begin aQuery:=TQuery.Create(nil); try aQuery.Database := 'myTrallalal' aQuery.SQL.Text := 'select INR,aa,bb,cc from myTable where PR=:pPR'; aQuery.params[0].asinteger = aPR; aQuery.Open; while not aQuery.eof do begin // hier jetzt print Daten, den aLevel kann man // als Multiplikator für den Einzug benutzen .... printTree(aQuery.fieldbyname('INR',aLevel+1); // rekursiver Aufruf der Childs aQuery.next; end; finally aQuery.Free; end; end begin .. printTree(1,0); .. end; |
AW: Baumstruktur selber zeichnen
Wenn dein DBMS rekursive SQL-Befehle kennt, dann kannst du für jeden Knoten zählen wie viele Eltern der hat und schon weißt du die Tiefe.
Außerdem fragst du das selber schon rekursiv ab, also mußt du dir nur merken in welcher Ebene du gerade bist und der aktuelle Knoten ist dann Eines tiefer. Im Grunde kann man auch gleich DB-seitig die Ebene ermitteln und zusammen mit den Eltern alle Datensätze fertig sortiert ausgeben und muß dann nur noch in Ruhe zeichnen, ohne das über mehrere SELECTS abzufragen. Ich hatte soeinen Code auch schonmal gesehn, wo man das Query auf CachedUpdate aktiviert hatte. Dann nahm man sich einen Datensatz, ging per Locate so lange hoch, bis man keinen Elternknoten fand. Danach dann rekursiv im selben Query per Locate die Kinder suchen und sie löschte, wenn eingefügt. (man könnte sich auch in einer Liste speichern, was schon verarbeitet wurde) Und danach das wiederholen, bis es keine Datensätze mehr gibt. (das Letzte war, falls es Probleme in der Stuktur/Daten gibt, damit dann dennoch alles ausgegeben wird) Ich hatte mir da auch or Kuzrem ein böses SELECT in Postgres geschrieben, um in einem DBGrid die selbe Reihenfolge zu haben, wie im TreeView.
SQL-Code:
[*] max(x_depth) ergäbe die Tiefe
SELECT
sd_id, sd_name, sd_parentid, ... (SELECT array_to_string(array_agg(to_char(x_id, 'FM00009')), '-')::VARCHAR(250) FROM ( WITH RECURSIVE temp (x_depth, x_id, x_parentid) AS ( (SELECT 0, x.sd_id, x.sd_parentid FROM SettingsDyn AS x WHERE x.sd_id = SettingsDyn.sd_id UNION SELECT 666, 0, NULL) -- virtueller Wurzel-Knoten UNION ALL (SELECT x_depth+1, x.sd_id, x.sd_parentid FROM SettingsDyn AS x JOIN temp ON x_parentid = x.sd_id WHERE x_depth < 32) ) SELECT * FROM temp ORDER BY x_depth DESC ) AS temp) AS sd_order FROM SettingsDyn ORDER BY sd_order
SQL-Code:
[*] oder x_id gibt einfach ALLE Eltern zurück (hier inkl. sich selber), was man dann sonstwie kombinieren kann,
(WITH RECURSIVE temp (x_depth, x_id, x_parentid) AS (
(SELECT 0, x.sd_id, x.sd_parentid FROM SettingsDyn AS x WHERE x.sd_id = SettingsDyn.sd_id) UNION ALL (SELECT x_depth+1, x.sd_id, x.sd_parentid FROM SettingsDyn AS x JOIN temp ON x_parentid = x.sd_id WHERE x_depth < 32) ) SELECT max(x_depth) FROM temp) AS DieTiefe hier z.B. als ein String, der für die Sortierung nutzbar ist. (der Tree ist über die IDs sortiert, also stehen hier die IDs der Prarents in der richtigen Reihenfolge im String)
Code:
Hier könnte man auch auf x_depth verzichtgen und über Length(sd_order)/6 die Tiefe holen, falls/da man nur eine Spalte zurückgeben kann.
00000
00001 00001_00002 00001_00002_00003 00001_00004 00002 .... Sonst müsste man das besser in eine Funktion auslagern oder es als LATERAL JOIN anhängen. |
AW: Baumstruktur selber zeichnen
Der Level (also die Y Position) ist nicht so das Problem. Die X Position ist da etwas komplexer.
Und die Struktur kann er schon lesen. Es geht um die Ermittlung der X Position der jeweiligen Elemente. (oh, nee, das Lesen der Struktur ist auch noch ein Knackpunkt) |
AW: Baumstruktur selber zeichnen
Zitat:
Delphi-Quellcode:
function printTree(const aPR, alevelY:integer; alevelX:integer):integer;
var aQuery:TQuery begin aQuery:=TQuery.Create(nil); try aQuery.Database := 'myTrallalal' aQuery.SQL.Text := 'select INR,aa,bb,cc from myTable where PR=:pPR'; aQuery.params[0].asinteger = aPR; aQuery.Open; while not aQuery.eof do begin inc(alevelX); // der Datensatz hat jetzt die Position (alevelx, alevely) // hier jetzt print Daten, den aLevel kann man // als Multiplikator für den Einzug benutzen .... alevelx:= printTree(aQuery.fieldbyname('INR',alevelY+1, alevelX); // rekursiver Aufruf der Childs aQuery.next; end; finally aQuery.Free; end; result := alevelx; end begin .. printTree(1,0,0); .. end; |
AW: Baumstruktur selber zeichnen
Es gab da mal einen 3-teiligen Artikel von Holger Klemt, ich meine im Entwickler, vor so 15 Jahren, in dem er das für Firebird recht einfach in einer StoredProcedure implementiert hat. Diese hat sich selbst rekursiv aufgerufen und den Level damit selbst berechnet. Von daher wäre die Abfrage (wenn bei deiner DB StoredProceures unterstützt werden, recht einfach möglich) - falls du das brauchst kann ich mal nachschauen wo ich das Teil habe.
Und bzgl. der Darstellung: Müssen das unbedingt Panels sein die du irgend wo rein positionierst? Reicht für die Darstellung ggf. der VIrtualStringtree nicht auch aus? Grüße |
AW: Baumstruktur selber zeichnen
Liste der Anhänge anzeigen (Anzahl: 2)
Hattest du an so etwas gedacht
Anhang 45558 Source und EXE im Anhang. Zur Info: Da ich nur ein kleiner Geist bin und ich mich immer nur auf eins konzentrieren kann, habe ich hierfür ein paar Klassen angelegt.
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 21:58 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz