Hierarchien sind das A und O bei der Datenbankentwicklung. Sie im Datenmodell abzubilden und diese in Formularen anzuzeigen, sind jedoch zwei verschiedene Welten. Da es aber ohne passende Formulare zur Eingabe und Bearbeitung auch hierarchischer Daten nicht geht, stellen wir Ihnen in diesem Beitrag verschiedene Möglichkeiten für die Abfrage und Darstellung der unterschiedlichen Hierarchieformen vor.
Die Vorarbeit zum Thema “Visualisierung von Hierarchien” liefert der Beitrag Datenhierarchien in Access (access-im-unternehmen.de/555), der die Realisierung der verschiedenen Hierarchiearten in entsprechend verknüpften Tabellen zeigt. Auf diesem Beitrag bauen wir auf und zeigen nun, wie sich die verschiedenen Hierarchien durch Abfragen aufbereiten und in Formularen und passenden Steuerelementen anzeigen lassen.
Darstellen von Begriffshierarchien
Begriffshierarchien lassen sich erfreulich leicht behandeln, da die Datensätze selbst gar nicht kaskadierend anwachsen, sondern nur die Tabellen, auf die sie verteilt sind.
Es wurden mehrere Typen von Begriffshierarchien im erwähnten Beitrag erläutert. Für die Darstellung in der Oberfläche von Access ist es ausreichend, sich mit der Alternative disjunkt versus adjunkt auseinanderzusetzen. Erinnern Sie sich: Adjunkt bedeutet, dass sich ein Element auf höherer Stufe in mehrere Detailtabellen fortsetzen kann. Im Beispiel Firma (Lieferant, Kunde) konnte eine Firma sowohl Kunden- als auch Lieferantendaten aufweisen oder beides zugleich oder keines von beiden.
Disjunkt bedeutet, dass ein Element höherer Stufe nur höchstens einen Detaildatensatz haben kann. Im Beispiel Tiersystematik Wirbeltier (Fisch|Vogel|Säuger) konnte ein Tier nur einen Folgedatensatz haben, da es zum Beispiel nicht gleichzeitig Fisch und Vogel sein kann. Einer der Detailbegriffe muss es aber sein.
Abfragen von Begriffsbeziehungen
Eine Abfrage über alle Tiere mit angehängten Spezialisierungen muss zwingend mit Outer Joins arbeiten, da ein Hauptdatensatz ja nur in einer Detailtabelle fortgesetzt werden kann. Die Entwurfsansicht sehen Sie in Bild 1, das Ergebnis in Bild 2.
Bild 1: Entwurf alle, disjunkt
Bild 2: Ergebnis alle, disjunkt
Beachten Sie die systematisch leeren Felder in dieser Abfrage. Bei einem falschen Entwurf sähe die Tabellenstruktur selbst so aus.
Sinnvoller sind in der Regel spezialisierte Abfragen auf alle Fische, alle Vögel oder alle Tiere, aber ohne Spezialfelder. Eine Abfrage auf alle Fische filtert automatisch, wenn Sie einfach einen Inner Join benutzen. Das Ergebnis des folgenden SQL-Ausdrucks finden Sie in Bild 3.
Bild 3: Ergebnis Fische, disjunkt
SELECT Tier.idTier, Tier.Name, Tier.Gewicht, Fisch.Schuppenform, Fisch.Flossenzahl, Fisch.Wasser FROM Tier INNER JOIN Fisch ON Tier.idTier = Fisch.fiFisch;
Die Abfrage gemeinsamer Eigenschaften aller Tiere ohne Spezialfelder ist trivial. Hier fragen Sie einfach die Tabelle Tiere alleine ab.
Beim Abfragen einer adjunkten Hierarchie ändert sich nichts, mit dem Unterschied, dass die Abfrage aller Hierarchietabellen zugleich, wie in Bild 1 und 2, durchaus sinnvoll beziehungsweise sogar der Normalfall ist.
Aus Gründen der Übersichtlichkeit haben Sie bisher nur eine Mini-Hierarchie mit zwei Ebenen gesehen. Um den hierarchischen Charakter des Aufbaus noch etwas zu verdeutlichen, sehen Sie ein etwas komplexeres Beispiel in Bild 4.
Bild 4: Begriffshierarchie mit drei Stufen
Formulardarstellung
Es empfiehlt sich eine unterschiedliche Darstellungsweise für die beiden Hierarchietypen disjunkt und adjunkt.
Disjunkte Formulare
Für die Präsentation gibt es im Wesentlichen zwei sinnvolle Möglichkeiten, die sich natürlich weiter ausbauen lassen. Eine gemeinsame Darstellung in einem Formular nach dem Schema Tierfelder+Fischfelder+Vogelfelder+Säugerfelder wie in der Abfrage aus Bild 1 und 2 ist normalerweise weniger geeignet, da bei jedem Datensatz ein Teil der Spezialisierungsfelder systematisch leer bliebe. Die Detailfelder können nach Art und Menge in den Gruppen völlig unterschiedlich sein.
Bei den gewählten Beispielen besteht in den spezialisierten Zweigen immer noch eine gewisse semantische Symmetrie. Bedenken Sie aber, dass eine Spezialisierung durchaus so aussehen könnte, dass Typ A über drei umfangreiche Textfelder verfügt, während Typ B statt dessen durch vierzig Kennzahlen, die in Form von Balken dargestellt werden sollen, charakterisiert wird. Es ist also wenig hilfreich, Detailfelder wechselseitig auszublenden oder ähnliches. Hier sind nur typenspezifisch gestaltete Formulare sinnvoll.
Aber zurück zu den Tieren: Benutzer, die sich für die Inhalte von Fischfeldern interessieren, brauchen also gar keine Vögel und Säuger zu sehen.
Benutzer wiederum, die sich für Vögel, Säuger und Fische synoptisch interessieren, können eigentlich nur an den gemeinsamen Tierfeldern interessiert sein, da alle anderen keine gruppenübergreifende Vergleichsbasis bieten.
Um dem entgegenzukommen, bieten sich zwei Vorgehensweisen an:
- Völlig getrennte Spezialformulare für jede Gruppe:
- frmFisch: Tierfelder+Fischfelder
- frmVogel: Tierfelder+Vogelfelder
- frmSäuger: Tierfelder+Säugerfelder
- Gemeinsames Formular für alle Tiere ohne Detailinformationen
- frmTier: Tierfelder
In beiden Fällen sehen die Formulare so aus, als bezögen sie einfach nur eine Tabelle – die Begriffshierarchie bleibt völlig im Hintergrund.
Sie könnte sich aber zum Beispiel in einer Menüstruktur zeigen (siehe Bild 5). Für die Arbeit mit den konkreten Daten ist die hierarchische Struktur bedeutungslos.
Bild 5: Menü für Begriffshierarchie
Ein Menüpunkt “Alle” öffnet dann sinnigerweise ein Formular, das die Eigenschaften anzeigt, die für alle Tiere gelten, während ein Menüpunkt “Vögel” ein Formular öffnen könnte, das die gemeinsamen Eigenschaften aller Tiere plus die speziellen Eigenschaften von Vögeln anzeigt.
Hinter einem solchen Formular steht eine Abfrage wie die bereits unter Abfragen von Begriffsbeziehungen vorgestellte. Sie können darin ohne Weiteres neue Datensätze anlegen, wobei ein Autowert in der Haupttabelle automatisch als Fremdschlüssel in der verknüpften Detailtabelle auftaucht, sobald ein Spezialfeld gefüllt wird.
Adjunkte Formulare
Hier sieht die Sache anders aus, da ein Datensatz mehrere Spezialdatensätze haben kann, die möglicherweise auch gemeinsam interessieren. Natürlich können Sie dennoch Spezialformulare wie im vorigen Kapitel erstellen. Eine Übersicht aller Lieferanten mit ihren Lieferbedingungen und natürlich auch den allgemeinen Firmeninformationen ergibt durchaus Sinn, entsprechend auch für Kunden. Das entspricht analog einer reinen Fisch- oder Vogelabfrage.
Allerdings ist die Zusammenfassung der Details in der Gesamtansicht durchaus von Interesse, da ein Hauptdatensatz ja über alle Details gleichzeitig verfügen kann. Bei der Formulargestaltung ist wichtig, dass Sie durch Rahmenelemente, farbige Bereiche oder ähnliche Maßnahmen die hierarchische Zusammengehörigkeit der Datenblöcke ausdrücken. Einen einfachen Vorschlag hierzu sehen Sie in Bild 6.
Bild 6: Adjunkte Hierarchie im Formular
Eine platzsparende Alternative mit Registern ist in Bild 7 dargestellt.
Bild 7: Adjunkte Hierarchie mit Register
In beiden Beispielen werden keine Unterformulare verwendet und die Datenbasis der Formulare ist jeweils die Abfrage aus Bild 4. Die Register sind übrigens nicht verschachtelt, das zweite Register ist einfach über dem ersten platziert. Beim Registerwechsel schaltet das untergeordnete Register dennoch um: Es hat fünf Seiten, die mit der Routine aus dem folgenden Listing gesteuert werden:
Private Sub RegisterMain_Change() Dim i As Long With Me!RegisterMain For i = 0 To 2 .Pages(i).Visible = _ (Me!RegisterSub.Value = 0) Next i For i = 3 To 4 .Pages(i).Visible = _ (Me!RegisterStrSub.Value = 1) Next i End With End Sub
Darstellen von Objekthierarchien
So einfach, wie mit den Begriffshierarchien haben Sie es mit den Objekthierarchien nicht. Im Gegenteil: Hier bietet die relationale Theorie allerlei auf, um Ihnen das Leben schwer zu machen. Abgeschlossene Hierarchien sind dabei noch vergleichsweise harmlos; offene reflexive Hierarchien entziehen sich aber einem reinen SQL-Zugriff völlig. Es liegt in der Natur der Sache, dass reflexive Strukturen nur mit Selbstbezug (Rekursion) oder Schleifen (Iteration) behandelbar sind. Das ist mit ANSI-SQL jedoch nicht möglich, sodass zusätzlich die Verwendung einer prozeduralen Sprache erforderlich wird – etwa VBA, um ein TreeView zu befüllen.
Arten der Visualisierung
Um Objekthierarchien darzustellen, müssen Sie im Wesentlichen zwei Informationen grafisch übersichtlich aufbereiten (mit der Frage, wie Sie diese aus den Hierarchietabellen gewinnen können, werden Sie sich noch beschäftigen müssen: sie stehen nämlich nicht einfach so drin). Das ist zum einen die Reihenfolge der Daten: Nachkommen sollten eine direkte Verbindung zu ihrem Elter haben und Geschwister beieinanderstehen. Zum anderen die Ebene: Angehörige der gleichen Generation sollten als solche erkennbar sein, auch wenn sie keine Geschwister sind.
Es gibt zwei übliche Darstellungsweisen, einmal vertikal in Form einer Liste und einmal horizontal in Form einer Tabelle. Die vertikale Darstellung ist als Baumansicht (TreeView) bekannt und verbreitet. Die Verwandtschaft zeigt sich durch Anordnung untereinander und Einrückung.
Vater1 Kind11 Enkel111 Kind12 Vater2 Kind21
Die horizontale Darstellung hat die Form einer hierarchischen Tabelle (Hierarchical Grid, wie es zum Beispiel mit Visual Basic 6 eingeführt wurde) und ist weniger verbreitet. Die Verwandtschaft wird hier durch Anordnung nebeneinander und Zellverschmelzung ausgedrückt.
Die grundsätzliche Befüllungslogik ist für beide Steuerelemente identisch: Sie müssen die Daten in passender Reihenfolge unter Berücksichtigung der Ebene eintragen. Auf die hierarchische Tabelle soll nicht mehr weiter eingegangen werden. Das Hierarchical Flexgrid weist erhebliche Einschränkungen auf und vergleichbare Controls von Drittherstellern gibt es zwar, aber die Programmierung weicht jeweils deutlich voneinander ab.
Ein Spezialfall ist die Beschränkung der Darstellung auf zwei Ebenen. In Access würden Sie hierzu ein Formular in Einzelansicht mit einem Unterformular in Endlos- oder Datenblattansicht verwenden.
Vater1 Kind11 Kind12
Wenn Sie in einem Endlosformular jedem Kinddatensatz einen Button verpassen, der das Kind zum aktuellen Datensatz im Hauptformular macht, haben Sie damit übrigens durchaus eine Funktionalität erzeugt, die eine beliebig tiefe Hierarchie handhabbar macht. Sie schieben sozusagen ein “Fenster” über die Hierarchie, das immer nur den Blick auf zwei benachbarte Ebenen erlaubt, aber ansonsten beliebig positioniert werden kann. Solange Sie nicht wirklich einen Gesamtüberblick brauchen, ist das durchaus eine hinlängliche Alternative. Sie werden zu jedem Fall später noch ein Beispiel sehen.
Datenabfragen bei Hierarchien
Hierbei beschränken wir uns auf die beiden praxisrelevanten Fälle. Das ist zum einen die abgeschlossene irreflexive Hierarchie, bei der die Nutzdaten der Objekte mit den Hierarchieinformationen gemeinsam in den Tabellen jeder Ebene gespeichert sind, wobei die Anzahl der Ebenen beliebig, aber begrenzt (also bekannt) ist, und zum anderen die offene reflexive Hierarchie, bei der es eine Nutzdatentabelle und eine reine Hierarchietabelle gibt. Grundlegende Informationen, die zu einem Element immer wieder gebraucht werden, sind:
- Wer ist mein Vater (Vorgänger)
- Wer sind meine Ahnen (Pfad)
- Wer sind meine Kinder
- Wie viele Kinder habe ich
- Wer sind meine Geschwister
- Wie viele Geschwister habe ich
- Wer sind meine Nachkommen
- Wie viele Nachkommen habe ich
- Zu welcher Generation gehöre ich
Die Abfrage dieser Informationen mit SQL unterscheidet sich gegebenenfalls bei geschlossenen und offenen Hierarchien. Beim Arbeiten mit Recordsets in VBA können Teile dieser Informationen sich auch aus dem Programmablauf ergeben.
Abgeschlossene Hierarchien
Eine vollständige Hierarchietabelle ergibt sich sehr einfach und anschaulich, indem Sie alle Tabellen der Hierarchie hintereinander mit einem LEFT JOIN verbinden – LEFT JOIN deshalb, damit Sie auch kinderlose Datensätze höherer Ebenen sehen.
SELECT Ebene0.id0, Ebene0.Bezeichnung0, Ebene1.id1, Ebene1.Bezeichnung1, Ebene2.id2, Ebene2.Bezeichnung2 FROM (Ebene0 LEFT JOIN Ebene1 ON Ebene0.id0 = Ebene1.fiParent1 ) LEFT JOIN Ebene2 ON Ebene1.id1 = Ebene2.fiParent2
Das Entwurfsdiagramm dazu sieht wie in Bild 8 aus, das Ergebnis wie in Bild 9. Ein anschauliches Beispiel dazu mit Kunden, Bestellungen und Artikeln sehen Sie in Bild 10.
Bild 8: Hierarchieüberblick, Entwurf
Bild 9: Hierarchieüberblick, Ergebnis
Bild 10: Ein weiterer Hierarchieüberblick
Dieses Vorgehen ist möglich, da die Anzahl der Ebenen beim Abfrageentwurf feststeht.
Das Abfragen des Vorgängers ist banal: Sein Schlüssel steht im Fremdschlüssel des aktuellen Datensatzes, weitere Daten, etwa eine Bezeichnung, ergeben sich durch einen schlichten JOIN auf die Vorgängertabelle.
Der Pfad ergibt sich entsprechend durch einen JOIN bis zur Ursprungsebene. Die Kinder erhalten Sie mit folgendem SQL-Ausdruck, wobei X für die abzufragende Ebene steht:
SELECT * FROM Ebene(X) WHERE fi=Ebene(X-1).id
Erzeugen eines Baumes in einer Abfrage
Theoretisch ist das recht einfach: Sie müssen die Daten der verschiedenen Ebenen nur alle in die richtige Reihenfolge untereinander bringen und den Abstammungsverhältnissen gemäß einrücken.
Praktisch ist das zwar möglich, aber nicht unbedingt einfach.
Um alle Daten in eine Abfrage zu bringen, brauchen Sie zunächst eine Union-Abfrage über alle Ebenen:
SELECT Bezeichnung FROM Ebene0 UNION SELECT Bezeichnung FROM Ebene1 UNION SELECT Bezeichnung FROM Ebene2 UNION SELECT Bezeichnung FROM Ebene3
Die Ebene ergibt sich aus der Ursprungstabelle:
SELECT Bezeichnung, 0 As E FROM Ebene0 UNION SELECT Bezeichnung, 1 As E FROM Ebene1 €...
Damit käme etwa folgende Darstellung heraus:
A (1) B (7) C (14) Aa (2) Ab (5) Ac (6) Ba (8) Bb (9) Bc (13) Aaa (3) Aab (4) Bba (10) Bbb (11) Bbc (12)
In Klammern sehen Sie die Zeilennummern, die die Datensätze bei richtiger Anordnung haben müssten:
A (1) Aa (2) Aaa (3) Aab (4) Ab (5) Ac (6) B (7) Ba (8) Bb (9) Bba (10) Bbb (11) Bbc (12) Bc (13) C (14)
Sie benötigen also noch Sortierfelder, die die richtige Reihenfolge sicherstellen. Dabei ist zu beachten: Die Sortierung bezüglich der Vaterschaft ist durch die Hierarchie vorgegeben, das heißt, Kinder müssen direkt nach ihrem Vater einsortiert werden; die Sortierung innerhalb einer Generation ist beliebig, das heißt, Geschwister könnten untereinander nach unterschiedlichen Kriterien sortiert sein.
In einer Hierarchie Kunde|Ansprechpartner|Betreute Projekte möchten Sie vielleicht den Kunden nach Firma oder Nachname sortieren, die Ansprechpartner innerhalb der Firmen nach Wichtigkeit und die von einem Ansprechpartner betreuten Projekte nach Datum. Das folgende Beispiel erstreckt einen Baum über drei Ebenen:
SELECT Order0, Order1, Order2, Space(4*E) & Bez AS H FROM (SELECT Bezeichnung0 AS Bez, Order0, 0 AS Order1, 0 AS Order2, 0 AS E FROM Ebene0) AS E0 UNION SELECT Order0, Order1, Order2, Space(4*E) & Bez AS H FROM (SELECT Bezeichnung1 AS Bez, Ebene0.Order0, Ebene1.Order1, 0 AS order2, 1 AS E FROM Ebene0 INNER JOIN Ebene1 ON Ebene0.id0 = Ebene1.fiParent1) AS E1 UNION SELECT Order0, Order1, Order2, Space(4*E) & Bez AS H FROM (SELECT Bezeichnung2 AS Bez, Ebene0.Order0, Ebene1.Order1, Ebene2.Order2, 2 AS E FROM (Ebene0 INNER JOIN Ebene1 ON Ebene0.id0 = Ebene1.fiParent1) INNER JOIN Ebene2 ON Ebene1.id1 = Ebene2.fiParent2) AS E2 ORDER BY order0, order1, order2
Das Ergebnis dieser Abfrage sehen Sie in Bild 11. Die künstlichen Einrückungen kommen durch den Einsatz der VBA-Funktion Space() zustande.
Bild 11:
Ende des frei verfügbaren Teil. Wenn Du mehr lesen möchtest, hole Dir ...
den kompletten Artikel im PDF-Format mit Beispieldatenbank
diesen und alle anderen Artikel mit dem Jahresabo