Verknüpfte Daten suchen

Wenn Sie eine Anwendung programmieren, die ihre Daten nur über die Benutzeroberfläche ändert, sehen Sie die änderungen ja immer direkt in den entsprechenden Formularen und Steuerelementen. Aber manchmal erstellen, ändern oder löschen Sie Daten auch per Mausklick auf eine Schaltfläche und die dahinter liegende Prozedur erledigt den Rest. Gerade in der Entwicklungsphase möchen Sie dann natürlich schnell sehen, ob die Daten auch wirklich wie gewünscht editiert wurden. Dazu quält man sich dann meist durch die einzelnen Tabellen und prüft das Ergebnis. Wenn sich die Daten über verknüpfte Tabellen erstrecken, wird dies erst recht anstrengend. Aber nicht mit der Lösung, die wir in diesem Beitrag vorstellen!

Beispieldatenbank

Als Beispiel dient uns die oft verwendete Südsturm-Datenbank. Sie hat einige verknüpfte Tabellen, mit denen wir das eingangs erwähnte Problem des schwierigen Nachvollziehens geänderter oder neuer Daten leicht nachstellen können. Im ersten Beispiel wollen wir uns die verknüpften Tabellen in der Reihenfolge tblKunden, tblBestellungen, tblBestelldetails, tblArtikel ansehen (s. Bild 1).

Tabellen der Datenbank Südsturm

Bild 1: Tabellen der Datenbank Südsturm

Die fertige Lösung

Am Ende dieses Artikels sollen Sie eine Lösung haben, mit der Sie verknüpfte Tabellen mit einer Tiefe von vier Hierarchieebenen auswählen und analysieren können.

Dazu erhalten Sie ein Formular, das vier Unterformulare enthält, von denen jedes die Daten einer von bis zu vier Tabellen anzeigt (s. Bild 2). Das oberste Unterformular soll beispielsweise die Daten der Tabelle tblKunden anzeigen. Darunter finden Sie dann die Daten der Tabelle tblBestellungen. Wenn der Benutzer nun auf einen der Datensätze der Tabelle tblKunden klickt, soll das Unterformular mit den Daten der Tabelle tblBestellungen nur die Datensätze anzeigen, die zum aktuellen Kunden gehören.

Die fertige Lösung

Bild 2: Die fertige Lösung

Darunter geht es weiter: Das dritte Unterformular zeigt alle Daten der Tabelle tblBestelldetails an, die mit dem Datensatz des zweiten Unterformulars mit den Bestellungen verknüpft ist. Das vierte Unterformular liefert schließlich noch den dem Bestelldetail zugeordneten Datensatz der Tabelle tblArtikel.

Der Clou bei der Lösung ist, dass Sie die Tabellen, deren Daten in den Unterformularen angezeigt werden, nicht fest in das Formular programmieren müssen, sondern dass es für alle beteiligten Tabellen, Primärschlüsselfelder und Fremdschlüsselfelder Kombinationsfelder zur Auswahl gibt. Auf diese Weise können Sie mit dem Formular nicht nur verschiedene Konfigurationen von verknüpften Tabellen untersuchen, sondern diese auch noch zur Laufzeit neu einstellen, um eine andere Konfiguration zu begutachten.

Nun wäre es natürlich aufwendig, immer wieder alle Eigenschaften einer Konfiguration neu einzugeben, wenn man diese einmal wechseln will oder das Formular gar in einer anderen Datenbank als der aktuellen einsetzen möchte. Deshalb können Sie verschiedene Konfigurationen speichern und diese immer wieder neu abrufen. Wenn Sie das Formular erstmalig öffnen, fragt dieses Sie gleich nach dem Namen der ersten anzulegenden Konfiguration. Diese wird dann nach dem öffnen des Formulars oben im Kombinationsfeld angezeigt.

Sie können nun mit den je nach Ebene zwei bis drei Kombinationsfeldern die Tabelle auswählen, die das entsprechende Unterformular anzeigen soll. Danach legen Sie zunächst für die obere Tabelle das Primärschlüsselfeld fest.

Damit das Formular weiß, welche Datensätze des zweiten Unterformulars zu dem im ersten Unterformular angezeigten Datensatz gehören, geben Sie für das zweite Unterformular das Fremdschlüsselfeld an, über das es mit den Datensätzen der übergeordneten Tabelle verknüpft ist.

Auf die gleiche Weise verfahren Sie mit den folgenden Kombinationsfeldern für Primär- und Fremdschlüsselfelder, bis Sie für das vierte Unterformular nur noch das Fremdschlüsselfeld angeben müssen.

Wenn Sie bereits mehrere Konfigurationen angelegt haben, können Sie diese über das Kombinationsfeld mit der Beschriftung Konfiguration wechseln. Mit der Löschen-Schaltfläche entfernen Sie die aktuell ausgewählte Konfiguration aus der Datenbank.

Auf geht”s!

Auf den folgenden Seiten werden wir uns zunächst eine einfache, statische Variante dieser Lösung ansehen, um die Grundlagen der Vorgehensweise zu verdeutlichen. Im Anschluss schauen wir uns dann an, wie das Formular zur flexiblen Anzeige der Daten verknüpfter Tabellen programmiert wird.

Erstellen des Hauptformulars

Das Hauptformular dient zunächst nur als Container für die einelnen Unterformulare. Erstellen Sie also ein leeres Hauptformular und speichern Sie es unter dem Namen frmVerknuepfteTabellen.

Gleich fügen wir die einzelnen Unterformulare und die Funktion in Form von VBA-Code hinzu.

Erstellen der Unterformulare

Die Unterformulare sind prinzipiell alle gleich aufgebaut. Wir schauen uns dies am Beispiel des Formulars sfmKunden an, welches die Datensätze der Tabelle tblKunden abbilden soll. Dazu legen Sie ein neues, leeres Formular an und stellen als Datenherkunft die Tabelle tblKunden ein. Gegebenenfalls können Sie auch eine Abfrage erstellen, welche beispielsweise die Kunden nach dem Feld Firma sortiert. Danach fügen Sie wie in Bild 3 alle Felder der Datenherkunft zum Detailbereich der Entwurfsansicht der Tabelle hinzu. Stellen Sie die Eigenschaft Standardansicht auf Datenblatt ein. Speichern Sie das Formular dann etwa unter dem Namen sfmKunden und schließen Sie es.

Entwurf des Unterformulars für die Anzeige der Kundendatensätze

Bild 3: Entwurf des Unterformulars für die Anzeige der Kundendatensätze

Unterformular in Hauptformular

Dann öffnen Sie das Hauptformular in der Entwurfsansicht und ziehen das Unterformular sfmKunden aus dem Navigationsbereich in den Detailbereich des Hauptformulars. Das Ergebnis sieht etwa wie in Bild 4 aus.

Das Hauptformular mit einem ersten Unterformular

Bild 4: Das Hauptformular mit einem ersten Unterformular

Weitere Unterformulare

Auf die gleiche Weise gehen Sie mit den Unterformularen für die Tabellen tblBestellungen, tblBestelldetails und tblArtikel vor: Legen Sie diese an, fügen Sie Datenherkunft und Felder hinzu und ziehen Sie diese dann untereinander in das Formular frmVerknuepfteTabellen. Mit allen vier Unterformularen sieht das Formular dann in der Datenblattansicht wie in Bild 5 aus. Die vier Unterformulare zeigen hier jeweils alle Datensätze ihrer Datenquelle an.

Das Hauptformular mit allen vier Unterformularen

Bild 5: Das Hauptformular mit allen vier Unterformularen

Das ist natürlich schon praktischer als wenn man immer alle Tabellen einzeln öffnen muss. Allerdings wollen wir noch einen entscheidenden Schritt weitergehen: Wenn der Benutzer nun auf einen der Einträge der Tabelle tblKunden im Unterformular sfmKunden klickt, soll das nächste Unterformular nur noch die Datensätze der Tabelle tblBestellungen anzeigen, die über das Feld KundeID mit dem ausgewählten Kundendatensatz verknüpft sind.

Funktion hinzufügen

Dazu müssen wir ein paar Zeilen VBA-Code hinzufügen. Wir machen das erstmal auf die einfachste Weise. Das Ereignis, das wir benötigen, heißt Beim Anzeigen und wird immer dann ausgelöst, wenn ein neuer Datensatz in einem Formular den Fokus erhält.

Diesem weisen wir nun eine Ereignisprozedur zu, welche die Datensätze des untergeordneten Unterformulars so filtert, dass es nur noch die mit dem aktuell ausgewählten Datensatz verknüpften Datensätze anzeigt.

Der Plan ist also, eine Prozedur wie die folgende auszulösen, die eine Referenz auf das darunter angeordnete Formular speichert und für diese die Filter-Eigenschaft mit einem entsprechenden Filter-Ausdruck belegt:

Private Sub Form_Current()
     Dim frm As Form
     Set frm = Me.Parent!sfmBestellungen.Form
     frm.Filter = "KundeID = " & Me!KundeID
     frm.FilterOn = True
End Sub

Wenn wir das Formular nun öffnen, erhalten wir allerdings die Fehlermeldung aus Bild 6. Das ist etwas überraschend: Wir haben das Unterformular doch korrekt referenziert Nach dem Akzeptieren des Fehlers mit Beenden schauen wir uns an, ob wir das Unterformular über den Direktbereich referenzieren können. Dazu setzen wir den folgenden Befehl ab:

Fehlermeldung beim öffnen des Formulars

Bild 6: Fehlermeldung beim öffnen des Formulars

  Forms!frmVerknuepfteTabellen! sfmBestellungen.Form.Name
   sfmBestellungen

Vom Direktfenster aus können wir also auf das Unterformular zugreifen. Also scheint es eine Frage des Timings zu sein. Das Ereignis Beim Anzeigen wird ja nicht nur dann ausgelöst, wenn der Benutzer den Datensatz wechselt, sondern auch direkt nach dem öffnen des Formulars. Offensichtlich ist das Unterformular sfmBestellungen also noch nicht geladen, wenn das Ereignis Beim Anzeigen des Unterformulars sfmKunden ausgelöst wird.

Wie können wir dieses Verhalten beeinflussen In welcher Reihenfolge werden die Unterformulare überhaupt geladen, wenn sich mehrere Unterformulare im gleichen Hauptformular befinden Dies wollen wir experimentell prüfen, indem wir ein neues, leeres Hauptformular erstellen und diesmal zuerst das Unterformular sfmBestellungen in den Entwurf des Formulars ziehen und erst dann sfmKunden.

Den Code brauchen Sie nicht erneut einzugeben, da er sich ja in dem Unterformular sfmKunden befindet. Wenn Sie das Formular nun öffnen, erscheint keine Fehlermeldung und das Unterformular sfmBestellungen zeigt nur die Bestellungen an, die zum aktuell im Unterformular sfmKunden ausgewählten Datensatz gehören.

Verfeinerung des Codes

Wir wollen uns allerdings nicht auf solch willkürliche Faktoren wie die Reihenfolge des Hinzufügens von Steuerelementen verlassen. Außerdem ist es ja auch kein erklärtes Ziel, gleich beim Anzeigen des Formulars alle Daten gleich auf den im ersten Formular ausgewählten Datensatz zu konzentrieren.

Schließlich ist es ohnehin kein guter Programmierstil, ein Unterformular von einem anderen Unterformular über den Umweg des Hauptformulars zu referenzieren. Und wenn wir die hier verwendete Prozedur auch für die verknüpften Tabellen in den übrigen Unterformularen nutzen wollen, verteilt sich der Code schnell über mehrere Formulare und wird unübersichtlich.

Daher wollen wir sämtlichen Code gleich ins Hauptformular überführen, von wo wir die Unterformulare referenzieren und die Ereignisprozeduren für das Ereignis Beim Anzeigen der verschiedenen Unterformulare implementieren wollen (s. Listing 1). Also löschen wir zunächst die Ereignisprozedur Form_Current aus dem Unterformular sfmKunden.

Dim WithEvents frm_sfmBestellungen As Form
Dim WithEvents frm_sfmKunden As Form
Dim WithEvents frm_sfmBestelldetails As Form
Dim frm_sfmArtikel As Form
Private Sub Form_Load()
     Set frm_sfmKunden = Me!sfmKunden.Form
     frm_sfmKunden.OnCurrent = "[Event Procedure]"
     Set frm_sfmBestellungen = Me!sfmBestellungen.Form
     frm_sfmBestellungen.OnCurrent = "[Event Procedure]"
     Set frm_sfmBestelldetails = Me!sfmBestelldetails.Form
     frm_sfmBestelldetails.OnCurrent = "[Event Procedure]"
     Set frm_sfmArtikel = Me!sfmArtikel.Form
End Sub
Private Sub frm_sfmKunden_Current()
     With frm_sfmBestellungen
         .Filter = "KundeID = " & frm_sfmKunden.KundeID
         .FilterOn = True
     End With
End Sub
Private Sub frm_sfmBestellungen_Current()
     With frm_sfmBestelldetails
         .Filter = "BestellungID = " & frm_sfmBestellungen.BestellungID
         .FilterOn = True
     End With
End Sub
Private Sub frm_sfmBestelldetails_Current()
     With frm_sfmArtikel
         .Filter = "ArtikelID = " & frm_sfmBestelldetails.ArtikelID
         .FilterOn = True
     End With
End Sub

Listing 1: Code, um die Unterformulare nacheinander zu filtern

Danach legen Sie eine Variable für das Unterformular sfmKunden im Klassenmodul des Hauptformulars frmVerknuepfteTabellen an:

Dim WithEvents frm_sfmKunden As Form

Damit diese gefüllt wird, implementieren wir das Ereignis Beim Laden des Hauptformulars wie folgt:

Private Sub Form_Load()
     Set frm_sfmKunden = Me!sfmKunden.Form
     frm_sfmKunden.OnCurrent = "[Event Procedure]"
End Sub

Nun müssen wir nur noch das Ereignis Beim Anzeigen des Unterformulars implementieren. Dazu wählen Sie im Klassenmodul des Hauptformulars aus dem linken Kombinationsfeld den Eintrag frm_sfmKunden aus und aus dem rechten Kombinationsfeld den Eintrag OnCurrent. Dies legt die neue Ereignisprozedur an, die Sie wie folgt ergänzen:

Private Sub frm_sfmKunden_Current()
     With frm_sfmBestellungen
         .Filter = "KundeID = " & frm_sfmKunden.KundeID
         .FilterOn = True
     End With
End Sub

Das war es schon – wir haben den kompletten Code im Hauptformular und können so steuern, dass das Unterformular sfmBestellungen nur die Bestellungen anzeigt, die dem im Unterformular sfmKunden ausgewählten Datensatz zugeordnet sind.

Eine Voraussetzung ist dafür zu erfüllen, damit Sie die Ereignisprozeduren der untergeordneten Formulare implementieren können: Diese müssen jeweils ein eigenes Klassenmodul enthalten.

Dies stellen Sie am schnellsten über den Wert Ja für die Eigenschaft Enthält Modul des jeweiligen Formulars ein.

Verknüpfung aller Unterformulare

Damit alle Unterformulare jeweils die Daten des folgenden Unterformulars filtern, benötigen wir für jedes Unterformular die bereits vorgestellten Code-Zeilen.

Wir deklarieren also für alle vier Unterformulare jeweils eine Form-Variable, von denen allerdings nur die ersten drei mit dem Schlüsselwort WithEvents ausgestattet werden müssen – das letzte Unterformular mit den Einträgen der Tabelle tblArtikel soll ja beim Anklicken keine weiteren Datensätze mehr filtern.

Die Prozedur Form_Load des Hauptformulars setzt dann die Verweise für die vier Form-Variablen auf die entsprechenden Form-Elemente in den Unterformular-Steuerelementen. Für die ersten drei legt sie außerdem fest, dass in diesem Klassenmodul auch nach Implementierungen des Ereignisses OnCurrent gesucht werden soll.

Schließlich fehlen noch die Implementierungen der Ereignisse, die jeweils ähnlich aufgebaut sind, aber sich auf die verschiedenen Unterformulare beziehen. frm_sfmKunden_Current stellt den Filter von frm_sfmBestellungen auf den aktuellen Kunden ein, frm_sfmBestellungen_Current den Filter von frm_sfmBestelldetails und frm_sfmBestelldetails_Current den von frm_sfmArtikel.

Benennung der Form-Variablen

Warum haben wir die Formular-Variablen nicht einfach genauso benannt wie die Unterformulare Wir haben es zunächst ausprobiert, aber Access kommt anscheinend nicht klar, wenn es einerseits ein Unterformular-Steuerelement und andererseits eine mit WithEvents deklarierte Variable mit dem gleichen Namen gibt.

Wir haben bei diesem Versuch reproduzierbar einen Absturz von Access verursacht, als wir versucht haben, das Ereignis Beim Anzeigen für das Element sfm_Bestellungen anzulegen. Aus diesem Grund haben wir allen Form-Variablen noch das Präfix frm_ vorangestellt.

Test des Formulars

Nach dem Wechsel in die Formularansicht funktioniert das Formular wie gewünscht. Wenn Sie einen der Einträge des obersten Unterformulars mit den Daten der Tabelle tblKunden anklicken, zeigt das nächste Unterformular die dazu gehörende Bestellung, das nächste die zur Bestellung gehörenden Bestelldetails und das letzte den Artikel zum jeweiligen Bestelldetail an (s. Bild 7).

Gefilterte Unterformulare

Bild 7: Gefilterte Unterformulare

Flexible Gestaltung

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