Hierarchien visualisieren

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.

pic001.tif

Bild 1: Entwurf alle, disjunkt

pic002.tif

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.

pic003.tif

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.

pic004.tif

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.

pic005.tif

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.

pic006.tif

Bild 6: Adjunkte Hierarchie im Formular

Eine platzsparende Alternative mit Registern ist in Bild 7 dargestellt.

pic007.tif

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.

pic008.tif

Bild 8: Hierarchieüberblick, Entwurf

pic009.tif

Bild 9: Hierarchieüberblick, Ergebnis

pic010.tif

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.

pic011.tif

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

Schreibe einen Kommentar