Lies diesen Artikel und viele weitere mit einem kostenlosen, einwöchigen Testzugang.
Kreuztabellenabfragen sind eine praktische Sache, wenn es darum geht, Kombinationen aus m:n-Beziehungen abzubilden – zum Beispiel die Preise für verschiedene Bauelemente oder Materialien in Abhängigkeit von der Höhe und der Breite des Materials. Dabei benötigen Sie noch nicht einmal eine m:n-Beziehung, die in einer Kreuztabelle darzustellenden Daten können auch aus einer einzigen Tabelle stammen. Der Haken an Kreuztabellen ist, dass diese in der Regel nicht bearbeitet werden können. Wenn Sie also etwa den Preis für ein Bauelement mit einer Höhe von einem Meter und einer Breite von 50 Zentimetern einstellen wollen, müssen Sie wieder die zugrunde liegende Tabelle oder das darauf aufbauende Formular bemühen. Im vorliegenden Beitrag wollen wir zunächst einmal die Grundlage für die Bearbeitung schaffen – indem wir die Daten der Tabelle per HTML in Kreuztabellenform darstellen.
Basistabelle
Als Basistabelle verwenden wir die Tabelle tblMaterialpreise. Diese enthält die Felder MaterialpreisID, Breite, Hoehe und Preis. Die Datentypen können Sie dem Tabellenentwurf aus Bild 1 entnehmen.
Bild 1: Entwurf der Tabelle tblMaterialpreise
Hier sehen Sie auch, dass wir für die beiden Felder Hoehe und Breite einen zusammengesetzten, eindeutigen Index definiert haben. Damit stellen wir sicher, dass für jede Kombination aus Höhe und Breite nur ein Preis festgelegt werden kann – mehr können wir in der Kreuztabelle nicht darstellen.
Daten in Kreuztabellenform ausgeben
Die Daten sollen nun so ausgegeben werden, dass die Spaltenüberschriften die Höhe, die Zeilenüberschriften die Breite und die Zellen selbst den jeweiligen Preis für die Kombination aus Höhe und Breite anzeigen. Dazu fügen wir der Tabelle erst einmal ein paar Datensätze zum Spielen hinzu. Die gefüllte Tabelle sieht dann wie in Bild 2 aus.
Bild 2: Beispieldaten in der Tabelle tblMaterialpreise
Danach wollen wir zuerst einmal die Daten in Kreuztabellenform im Direktbereich des VBA-Editors ausgeben. Erst nachdem wir den grundlegenden Ablauf programmiert haben, nehmen wir die Darstellung in einer HTML-Tabelle hinzu.
Die Ausgabe im Direktbereich soll durch die Prozedur Kreuztabelle erfolgen (siehe Listing 1). Diese definiert eine Variable für das aktuelle Datenbankobjekt (db) sowie drei Recordset-Variablen. Das Recordset rstSpaltenkoepfe nimmt alle Werte des Feldes Hoehe der Tabelle tblMaterialpreise auf. Damit jeder Wert nur einmal vorkommt, fügen wir neben SELECT als zweites Schlüsselwort noch DISTINCT hinzu. Auf diese Weise liefert das Recordset jeden Wert des Feldes Hoehe nur einmal, hier 800, 900 und 1.000. Außerdem sortieren wir die Werte nach der Größe. Damit können wir schon einmal die Spaltenüberschriften in den Direktbereich schreiben. Hier beginnen wir mit einer leeren Spalte, was wir durch die Ausgabe einer leeren Zeichenkette plus dem Komma-Zeichen (,) stellvertretend für einen Tabulator-Schritt erledigen. Dann durchlaufen wir in einer Do While-Schleife alle Datensätze des Recordsets rstSpaltenkoepfe und geben nacheinander alle Werte in einer Zeile aus. Damit Debug.Print nach der Ausgabe eines Wertes nicht in die nächste Zeile springt, hängen wir auch hier jeweils das Komma an. Dadurch wird auch hier ein Tabulator eingefügt statt des üblichen Zeilenumbruchs. Damit wäre die Zeile mit den Spaltenköpfen bereits geschafft. Das Ergebnis sieht bisher so aus:
Public Sub Kreuztabelle() Dim db As DAO.Database Dim rstSpaltenkoepfe As DAO.Recordset Dim rstZeilen As DAO.Recordset Dim rstWerte As DAO.Recordset Set db = CurrentDb Set rstSpaltenkoepfe = db.OpenRecordset("SELECT DISTINCT Hoehe FROM tblMaterialpreise ORDER BY Hoehe", dbOpenDynaset) Debug.Print , Do While Not rstSpaltenkoepfe.EOF Debug.Print rstSpaltenkoepfe!Hoehe, rstSpaltenkoepfe.MoveNext Loop Debug.Print Set rstZeilen = db.OpenRecordset("SELECT DISTINCT Breite FROM tblMaterialpreise ORDER BY Breite", dbOpenDynaset) Do While Not rstZeilen.EOF Debug.Print rstZeilen!Breite, Set rstWerte = db.OpenRecordset("SELECT Preis FROM tblMaterialpreise WHERE Breite = " & rstZeilen!Breite _ & " ORDER BY Hoehe", dbOpenDynaset) Do While Not rstWerte.EOF Debug.Print rstWerte!Preis, rstWerte.MoveNext Loop Debug.Print rstZeilen.MoveNext Loop End Sub
Listing 1: Ausgabe der Spaltenköpfe, Zeilenköpfe und Preise im Direktbereich
800 900 1000
Damit wir danach mit der ersten Zeile mit Daten fortfahren können, folgt nun der Aufruf der Debug.Print-Anweisung ohne Parameter. Damit springen wir direkt in die nächste Zeile. Das zweite Recordset namens rstZeilen füllen wir mit allen Einträgen des Feldes Breite der Tabelle tblMaterialpreise, diesmal aufsteigend sortiert nach dem Feld Breite. Auch hier verwenden wir das DISTINCT-Schlüsselwort, damit jede Breite nur einmal aufgenommen wird. Dieses Recordset enthält nun also jeweils einen Datensatz für jede Breite, hier 800, 900 und 1.000.
Dieses Recordset durchlaufen wir nun in der nächsten Do While-Schleife und wollen in jedem Durchlauf eine komplette Zeile mit dem Zeilenkopf und den Werten für die Kombination aus Spaltenkopf und Zeilenkopf in den Direktbereich schreiben. Den Zeilenkopf geben wir schon einmal per Debug.Print aus, wobei wir dieser als Parameter den Wert rstZeilen!Breite plus einem Komma für einen Sprung zur nächsten Tabulatorposition statt in die nächste Zeile.
Außerdem brauchen wir für jede Zeile noch ein weiteres Recordset, das die Werte für die aktuelle Zeile und die jeweilige Spalte abruft. Dieses heißt rstWerte und enthält alle Werte des Feldes Preis der Tabelle tblMaterialpreise, deren Feld Breite den Wert des aktuellen Spaltenkopfes enthält – wieder sortiert nach dem Feld Hoehe.
Auch diese Werte durchlaufen wir in einer Do While-Schleife und geben in dieser jeweils den Inhalt des Feldes Preis aus – gefolgt von einem Komma, damit kein Zeilen-umbruch, sondern ein Sprung an die nächste Tabulator-Stelle erfolgt. Das Ergebnis nach dem Durchlaufen sieht dann wie folgt aus:
800 900 1000 800 64 72 80 900 72 81 90 1000 80 90 100
Gar nicht schlecht für den Start! Allerdings enthält diese Vorgehensweise eine kleine Schwachstelle. Wir ergänzen die Tabelle tblMaterialpreise wie in Bild 3. Wie Sie sehen, haben wir mit 700 eine neue Höhe eingeführt, aber nur Werte für die Breiten 800 und 1.000 angegeben – die Kombination aus 700 und 900 fehlt. Das Ergebnis im Direktfenster sieht dann wie in Bild 4 aus. Der Preis für die Kombination 700 und 900 wird einfach weggelassen und die übrigen Kombinationen für die Höhe von 700 mm werden einfach nach links verschoben.
Bild 3: Erweiterte Beispieldaten in der Tabelle tblMaterialpreise
Bild 4: Ausgabe der neuen Beispieldaten
Alle Kombinationen ohne Ausnahme ausgeben
Wie bekommen wir es also hin, dass alle Kombinationen berücksichtigt werden, auch wenn ein Wert nicht angegeben ist – in der Form, dass die entsprechende Stelle einfach frei gelassen wird
Dazu müssen wir eine kleine Ergänzung vornehmen, und zwar innerhalb der zweiten Do While-Schleife (siehe Listing 2). In dieser fügen wie zunächst eine Anweisung ein, welche den Datensatzzeiger des Recordsets, das wir zum Einfügen der Spaltenköpfe verwendet haben, für jeden Datensatz des Recordsets rstZeilen auf den ersten Datensatz zurücksetzt (MoveFirst). Warum das Weil wir sicherstellen wollen, dass die Markierung im Direktfenster für jeden Spaltenkopf in jeder Zeile auch um eine Tabulatorposition vorrückt – unabhängig davon, ob es auch einen Wert für die Kombination aus den im Spalten- und Zeilenkopf angezeigten Werten gibt. Innerhalb der inneren Do While-Schleife, in der wir bisher einfach nur die Werte des Feldes Preis in den Direktbereich geschrieben haben, fügen wir nun eine If…Then-Bedingung ein, die prüft, ob der Wert des Feldes Hoehe des Recordsets rstSpaltenkoepfe mit dem des Recordsets rstWerte übereinstimmt. Ist dies der Fall, wird der Inhalt des Feldes Preis ausgeben und wir gehen mit der MoveNext-Methode einen Schritt weiter im Recordset rstWerte. Falls nicht, geben wir nur einen Tabulatorschritt im Direktfenster aus. In beiden Fällen bewegen wir den Datensatzzeiger des Recordsets rstSpaltenkoepfe um eine Position weiter. Auf diese Weise geben wir für Kombinationen, für die es keinen Preis gibt, einen Tabulatorsprung im Direktbereich aus. Das Ergebnis sieht nun wie in Bild 5 aus.
Bild 5: Ausgabe der neuen Beispieldaten mit Leerstellen
Public Sub Kreuztabelle() ''''... wie in der ersten Fassung Set rstZeilen = db.OpenRecordset("SELECT DISTINCT Breite FROM tblMaterialpreise ORDER BY Breite", dbOpenDynaset) Do While Not rstZeilen.EOF rstSpaltenkoepfe.MoveFirst Debug.Print rstZeilen!Breite, Set rstWerte = db.OpenRecordset("SELECT Preis, Hoehe FROM tblMaterialpreise WHERE Breite = " _ & rstZeilen!Breite & " ORDER BY Hoehe", dbOpenDynaset) Do While Not rstWerte.EOF If rstSpaltenkoepfe!Hoehe = rstWerte!Hoehe Then Debug.Print rstWerte!Preis, rstWerte.MoveNext Else Debug.Print , End If rstSpaltenkoepfe.MoveNext Loop Debug.Print rstZeilen.MoveNext Loop End Sub
Listing 2: Ausgabe der Spaltenköpfe, Zeilenköpfe und Preise im Direktbereich auch mit leeren Kombinationen
Damit können wir die grundlegende Ausgabe als erledigt betrachten und uns der ersten Erweiterung zuwenden – der Ausgabe im HTML-Format im Webbrowser-Steuerelement von Access.
Webbrowser-Steuerelement anlegen
Dazu legen wir ein neues Formular namens frmKreuztabelleHTML an und fügen diesem ein Webbrowser-Steuerelement aus der Toolbox hinzu. Danach stellen sie noch seine Eigenschaften Horizontaler Anker und Vertikaler Anker auf den Wert Beide ein, damit sich die Größe des Webbrowser-Steuerelements beim Verändern der Größe des Formulars an dieses anpasst (siehe Bild 6).
Bild 6: Hinzufügen des Webbrowser-Steuerelements
Dann wollen wir zunächst dafür sorgen, dass beim öffnen des Formulars eine leere Webseite erscheint. Dazu fügen wir dem VBA-Projekt der Anwendung zunächst einen Verweis auf die Bibliothek Microsoft HTML Object Library hinzu – und zwar über den Dialog, den Sie mit dem Menübefehl Extras|Verweise öffnen (siehe Bild 7). Danach legen wir eine neue Ereignisprozedur für das Ereignis Beim öffnen des Formulars an. Die dort verwenden Variablen deklarieren wir im allgemeinen Teil des Klassenmoduls des Formulars:
Bild 7: Verweis auf die Bibliothek Microsoft HTML Object Library
Ende des frei verfügbaren Teil. Wenn Du mehr lesen möchtest, hole Dir ...
Testzugang
eine Woche kostenlosen Zugriff auf diesen und mehr als 1.000 weitere Artikel
diesen und alle anderen Artikel mit dem Jahresabo