Abfrageverwalter

Geschieht Ihnen das auch regelmäßig Sie wollen mal eben per Abfrage ein paar Daten filtern, sortieren oder zusammenführen, aber die Abfrage nach dieser Anwendung gleich wieder löschen. Dummerweise vergessen Sie Letzteres und irgendwann ist das Datenbankfenster voller gespeicherter Abfragen. Oder Sie benötigen gleichzeitig die Ergebnisse verschiedener Abfragen, finden die vielen geöffneten Fenster im Access-Hauptfenster aber unübersichtlich. Für beides liefert unser Abfrageverwalter die Lösung: Sie können damit mal eben eine Abfrage erstellen, ohne dass diese dauerhaft im Navigationsbereich verbleibt. Oder Sie zeigen die Ergebnisse mehrerer Abfragen übersichtlich im Registersteuerelement eines einzigen Formulars an.

Damit Sie gleich eine Vorstellung davon bekommen, wie diese Lösung aussehen soll, schauen wir uns das Formular aus Bild 1 an. Hier finden Sie eine ganze Reihe Elemente, zuoberst ein Listenfeld. Dieses zeigt alle mit dem Formular erstellten und gespeicherten Abfragen an. Darunter folgen die Steuerelemente für die Verwaltung der Abfragen:

Benutzeroberfläche des Abfrageverwalters

Bild 1: Benutzeroberfläche des Abfrageverwalters

  • Neu: öffnet den Entwurf einer neuen, leeren Abfrage.
  • Entwurf: Zeigt den Entwurf der im Listenfeld markierten Abfrage an.
  • Anzeigen: öffnet die im Listenfeld ausgewählte Abfrage in der Datenblattansicht.
  • Löschen: Entfernt die Abfrage aus dem Listenfeld.
  • Zur übersicht hinzufügen: Zeigt das Ergebnis der ausgewählten Abfrage auf einer Registerseite im unteren Bereich des Formulars an.
  • Aus übersicht entfernen: Entfernt eine Abfrage aus dem Registersteuerelement und blendet die entsprechende Registerseite aus.

Speicherort der Abfragen

Nun werden Sie sich zu Recht fragen: Wenn die Abfragen schon nicht im Navigationsbereich der Datenbank auftauchen, wo werden diese dann gespeichert Ganz einfach: Wie legen für jede hier hinzugefügte Abfrage einen neuen Datensatz in einer speziell für diesen Zweck vorbereiteten Tabelle an.

Diese Tabelle heißt tblAbfragen und sieht in der Entwurfsansicht wie in Bild 2 aus. Das Feld Abfragebezeichnung soll die Bezeichnung aufnehmen und ist als eindeutiger Index ausgelegt. Auf diese Weise verhindern wir, dass die Tabelle mehrere Abfragen mit der gleichen Bezeichnung aufnimmt. Das Feld AbfrageSQL enthält später den SQL-Ausdruck, den Sie mithilfe der Entwurfsansicht für Abfragen zusammengestelt haben. Das Feld Abfrageeigenschaften nimmt die wichtigsten Eigenschaften der Abfrage auf, die Sie üblicherweise über das Eigenschaftsfenster der Abfrage eingeben – wie etwa in Bild 3 die Beschreibung der Abfrage.

Entwurf der Tabelle zum Speichern der Abfragen

Bild 2: Entwurf der Tabelle zum Speichern der Abfragen

Einstellen von Eigenschaften für die Abfrage

Bild 3: Einstellen von Eigenschaften für die Abfrage

Die meisten der hier angegebenen Eigenschaften landen indes ohnehin in Form entsprechender Schlüsselwörter im SQL-Text der Abfrage – zum Beispiel die Eigenschaft Spitzenwerte als TOP.

Das Feld Uebersichtsposition enthält einen Wert, der Folgendes angibt: Wenn das Feld leer ist, soll diese Abfrage nicht in der übersicht der Abfrageergebnisse im Registersteuerelement erscheinen. Nur wenn dieses Feld einen Zahlenwert enthält, soll dies geschehen. In diesem Fall werden die Abfragen in aufsteigender Reihenfolge entsprechend dem Wert des Feldes dort angezeigt.

Damit können wir uns nun an das Formular zur Verwaltung der Abfragen begeben.

Das Formular frmAbfragen

Das Formular sieht in der Entwurfsansicht wie in Bild 4 aus. Es enthält ein Listenfeld zur Anzeige der in der Tabelle tblAbfragen gespeicherten Abfragen, einige Schaltflächen sowie ein Registersteuerelement mit zehn Registerseiten.

Entwurf des Formulars frmAbfragen

Bild 4: Entwurf des Formulars frmAbfragen

Listenfeld zur Anzeige der Abfragen

Das Listenfeld heißt lstAbfragen und verwendet die Abfrage aus Bild 5 als Datensatzherkunft. Diese liefert das Primärschlüsselfeld AbfrageID und die Bezeichnung der Abfrage aus dem Feld Abfragebezeichnung, nach der die Ergebnisse dieser Abfrage auch sortiert werden. Die Eigenschaft Spaltenanzahl des Listenfeldes erhält den Wert 2, die Eigenschaft Spaltenbreiten den Wert 0cm.

Abfrage, die als Datensatzherkunft des Listenfeldes lstAbfragen dient

Bild 5: Abfrage, die als Datensatzherkunft des Listenfeldes lstAbfragen dient

Neue Abfrage anlegen

Ein Klick auf die Schaltfläche cmdNeu soll eine neue Abfrage in der Entwurfsansicht öffnen und dem Benutzer die Möglichkeit geben, die gewünschten Tabellen und Felder in den Entwurf zu ziehen. Dieser Teil ist einfach und wird durch die folgende Prozedur realisiert:

Private Sub cmdNeu_Click()
     Dim db As DAO.Database
     Dim qdf As DAO.QueryDef
     Set db = CurrentDb
     strAktuelleAbfrage = "qryTemp_New"
     On Error Resume Next
     db.QueryDefs.Delete strAktuelleAbfrage
     On Error GoTo 0
     Set qdf = db.CreateQueryDef(strAktuelleAbfrage)
     DoCmd.OpenQuery strAktuelleAbfrage, acViewDesign
End Sub

Die Prozedur definiert in strAktuelleAbfrage den temporären Namen der zu erstellenden Abfrage. Die Variable strAktuelleAbfrage ist modulweit deklariert:

Private strAktuelleAbfrage As String

Die Prozedur löscht dann eine eventuell noch vorhandene temporäre Abfrage mit dem Namen qryTemp_New und legt diese mit der Methode CreateQueryDef neu an. Danach öffnet die OpenQuery-Methode des DoCmd-Objekts diese Abfrage in der Entwurfsansicht.

Die auf diese Weise geöffnete Abfrage zeigt gar nicht erst das Tabelle anzeigen-Fenster an, sodass Sie die als Datenquelle zu verwendenden Tabellen oder Abfragen direkt aus dem Navigationsbereich in den Abfrageentwurf ziehen können (s. Bild 6).

Anlegen einer neuen Abfrage in der Entwurfsansicht

Bild 6: Anlegen einer neuen Abfrage in der Entwurfsansicht

Mit dem entsprechenden Kontextmenü-Eintrag können Sie dieses Fenster aber auch nachträglich noch anzeigen.

Was aber geschieht, wenn die Abfrage fertiggestellt ist – wie bekommt das Formular mit, dass es soweit ist und wie wird dann die Abfrage in der Tabelle tblAbfragen gespeichert Dazu nutzen wir das Ereignis Bei Aktivierung des Formulars frmAbfragen. Dieses wird beispielsweise ausgelöst, wenn der Benutzer über die Schaltfläche cmdNeu eine neue Abfrage erstellt hat und diese dann schließt. Das Formular wird dann wieder aktiviert und führt die genannte Ereignisprozedur aus.

Deren Code finden Sie in Listing 1. Die Prozedur prüft zunächst, ob die Variable strAktuelleAbfrage einen Wert enthält. Dies ist beispielsweise der Fall, wenn Sie kurz zuvor die Schaltfläche cmdNeu betätigt haben. Ist dies der Fall, prüft die Prozedur auch noch, ob die Abfrage aktuell noch geöffnet ist. Dazu nutzt sie die Hilfsfunktion IstAbfrageGeoeffnet:

Private Sub Form_Activate()
     Dim db As DAO.Database, qdf As DAO.QueryDef
     Dim strAbfrageSQL As String, strAbfragebezeichnung As String, strAbfrageeigenschaften As String
     Dim lngAbfrageID As Long, strSQL As String
     If Len(strAktuelleAbfrage) > 0 Then
         If IstAbfrageGeoeffnet(strAktuelleAbfrage) Then
             MsgBox "Bitte speichern und schließen Sie die temporäre Abfrage ''" & strAktuelleAbfrage & "''"
             Exit Sub
         End If
         Set db = CurrentDb
         Set qdf = db.QueryDefs(strAktuelleAbfrage)
         If CDec(qdf.DateCreated) = CDec(qdf.LastUpdated) Then
             Exit Sub
         Else
             strAbfrageSQL = qdf.SQL
             strAbfrageeigenschaften = Abfrageeigenschaften(qdf)
             lngAbfrageID = Nz(DLookup("AbfrageID", "tblAbfragen", "''qryTemp_'' & Abfragebezeichnung = ''" _
                 & qdf.Name & "''"), 0)
             If lngAbfrageID = 0 Then
                 If MsgBox("Soeben erstellte Abfrage sichern", vbYesNo) = vbYes Then
                     strAbfragebezeichnung = Mid(qdf.Name, 9)
                     strAbfragebezeichnung = InputBox("Bezeichnung der Abfrage: ", "Abfrage speichern", _
                         strAbfragebezeichnung)
                     Do While Not IsNull(DLookup("AbfrageID", "tblAbfragen", "Abfragebezeichnung = ''" _
                             & strAbfragebezeichnung & "''"))
                         strAbfragebezeichnung = InputBox("Unter diesem Namen ist bereits eine Abfrage vorhanden. " _
                             & "Geben Sie eine neue Bezeichnung ein: ", "Abfrage speichern", strAbfragebezeichnung)
                     Loop
                     If Len(strAbfragebezeichnung) = 0 Then
                         Exit Sub
                     End If
                     If Len(strAbfragebezeichnung) > 0 Then
                         strSQL = "INSERT INTO tblAbfragen(Abfragebezeichnung, AbfrageSQL, Abfrageeigenschaften) " _
                             & "VALUES(''" & strAbfragebezeichnung & "'', ''" & strAbfrageSQL & "'', ''" _
                             & Replace(Replace(strAbfrageeigenschaften, """", "''"), "''", "''''") & "'')"
                         db.Execute strSQL, dbFailOnError
                     End If
                 End If
                 Me!lstAbfragen.Requery
             Else
                 If MsgBox("Soeben geänderte Abfrage speichern", vbYesNo) = vbYes Then
                     strSQL = "UPDATE tblAbfragen SET AbfrageSQL = ''" & strAbfrageSQL & "'', Abfrageeigenschaften = ''" _
                         & strAbfrageeigenschaften & "'' WHERE AbfrageID = " & lngAbfrageID
                     db.Execute strSQL, dbFailOnError
                 End If
             End If
         End If
         strAktuelleAbfrage = ""
     End If
End Sub

Listing 1: Diese Prozedur prüft, ob der Benutzer neue Abfragen angelegt hat, und speichert gegebenenfalls die Daten.

Private Function IstAbfrageGeoeffnet(strAbfrage As _
         String) As Boolean
     IstAbfrageGeoeffnet = _
         SysCmd(acSysCmdGetObjectState, acQuery, strAbfrage)
End Function

Ist die Abfrage noch geöffnet, erscheint eine Meldung, die den Benutzer darüber informiert, dass die Abfrage gespeichert und geschlossen werden muss. Die Prozedur wird dann beendet.

Ist die Abfrage nicht mehr geöffnet, referenziert die Prozedur das QueryDef-Objekt zu dieser Abfrage mit der Variablen qdf und vergleicht das Datum der Erstellung mit dem änderungsdatum. Sind die Daten gleich, hat der Benutzer seit der Erstellung keine änderungen vorgenommen und die Abfrage braucht nicht gespeichert zu werden.

Anderenfalls liest die Prozedur den SQL-Code der Abfrage in die Variable strAbfrageSQL ein und die Eigenschaften in die Variable strAbfrageEigenschaften – dazu später mehr. Sie liest dann den Primärschlüsselwert einer eventuell bereits in der Tabelle tblAbfragen gespeicherten Abfrage in die Variable lngAbfrageID ein.

Nur wenn noch keine Abfrage gleichen Namens vorhanden ist, fragt die Prozedur den Benutzer, ob dieser die erstellte Abfrage sichern möchte. Dann fragt die folgende InputBox-Funktion den Namen ab, unter dem die Abfrage gespeichert werden soll. Die folgende Do While-Schleife wird so lange durchlaufen, bis der Benutzer eine Bezeichnung eingegeben hat, die noch nicht in der Tabelle vorhanden ist. Dies kann natürlich schon nach der bereits erfolgten Eingabe in die InputBox der Fall sein. Falls nicht, erscheint eine weitere InputBox und fragt erneut die zu verwendende Bezeichnung ab.

Die Schleife wird erst verlassen, wenn eine noch nicht verwendete Bezeichnung eingegeben wurde – oder gar keine. Dies kann der Benutzer durch eine entsprechende Eingabe erreichen oder aber durch das Anklicken der Abbrechen-Schaltfläche der InputBox.

Diesen Fall prüft die erste If…Then-Bedingung nach der Do While-Schleife und beendet die Prozedur im Falle einer leeren Abfragebezeichnung.

Erst wenn eine gültige Abfragebezeichnung vorliegt, kann es weitergehen. Dann stellt die Prozedur in strSQL eine INSERT INTO-Aktionsabfrage zusammen, welche die ermittelten Daten in einen neuen Datensatz der Tabelle tblAbfragen schreibt, führt diese aus und aktualisiert den Inhalt des Listenfeldes, das nun den neuen Datensatz anzeigen sollte.

Nun folgt noch der Fall, dass die Abfrage bereits gespeichert wurde. Dies geschieht, um etwas vorzugreifen, beispielsweise dann, wenn der Benutzer eine bereits gespeicherte Abfrage im Entwurf öffnet und diese ändert.

In diesem Fall fragt die Prozedur per MsgBox, ob die geänderte Abfrage gespeichert werden soll. Dies erledigt dann eine entsprechende UPDATE-Anweisung.

Schließlich stellt die Prozedur den Wert von strAktuelleAbfrage auf eine leere Zeichenkette ein, damit die Prozedur Form_Activate beim nächsten Aktivieren des Formulars ohne zwischenzeitliches Erstellen oder öffnen einer der gespeicherten Abfragen weiß, dass nichts zu tun ist.

Eigenschaften der Abfrage speichern

Vorhin wurde auch die Funktion Abfrageeigenschaften erwähnt, welche die Eigenschaften einer Abfrage ermitteln soll. Diese finden Sie in Listing 2. Die Funktion erwartet lediglich einen Verweis auf das QueryDef-Objekt dieser Abfrage und liefert eine Zeichenkette zurück, welche die Eigenschaften und ihre Werte etwa wie folgt enthält, also als Name-Wert-Paare, die durch das Pipe-Zeichen (|) voneinander getrennt sind:

Private Function Abfrageeigenschaften(qdf As DAO.QueryDef) As String
     Dim prp As DAO.Property
     Dim strEigenschaften As String
     For Each prp In qdf.Properties
         Select Case prp.Name
             Case "StillExecuting", "CacheSize", "Prepare", "DOL", "NameMap", "DateCreated", "LastUpdated", "Type", _
                "Updatable", "RecordsAffected", "RecordLocks", "RecordsetType", "Name"
             Case Else
                 On Error Resume Next
                 strEigenschaften = strEigenschaften & prp.Name & "=" & prp.Value & "|"
                 If Not Err.Number = 0 Then
                     Debug.Print prp.Name, prp.Value
                 End If
                 On Error GoTo 0
         End Select
     Next prp
     If Len(strEigenschaften) > 0 Then
         strEigenschaften = Left(strEigenschaften, Len(strEigenschaften) - 1)
     End If
     Abfrageeigenschaften = strEigenschaften
End Function

Listing 2: Einlesen der Eigenschaften einer Abfrage und ihrer Werte in eine Zeichenkette

SQL=SELECT ...|Connect=|ReturnsRecords=Wahr|ODBCTimeout=60|MaxRecords=0|
OrderByOn=Falsch|Orientation=0|DefaultView=2|GUID=|FilterOnLoad=Falsch|
OrderByOnLoad=Wahr|TotalsRow=Falsch|FilterOn=Wahr|Description=Beispielbeschreibung|...

Die Funktion durchläuft alle Property-Elemente der Properties-Auflistung und schließt einige Elemente aus, die entweder nicht eingelesen werden können oder keinen Nutzen haben. Dabei stellt sie in der Variablen strEigenschaften eine Zeichenkette wie oben dargestellt zusammen. Diese wird anschließend noch um das zuletzt hinzugefügte Pipe-Zeichen gekürzt und als Funktionswert an die aufrufende Prozedur zurückgegeben. Damit ist der Vorgang zum Speichern der Definition einer Abfrage in der Tabelle tblAbfragen abgeschlossen.

Entwurf einer Abfrage öffnen

Die zweite Schaltfläche des Formulars heißt cmdEntwurf und soll die aktuell im Listenfeld markierte Abfrage in der Entwurfsansicht anzeigen. Dies geschieht primär durch die folgende Ereignisprozedur, die allerdings zwei weitere Routinen aufruft:

Private Sub cmdEntwurf_Click()
     Dim strAbfrage As String
     strAbfrage = AbfrageErstellen(Nz(Me!lstAbfragen, 0))
     If Not Len(strAbfrage) = 0 Then
         AbfrageOeffnen strAbfrage, acViewDesign
     End If
End Sub

Die erste heißt AbfrageErstellen und soll basierend auf den in der Tabelle tblAbfragen gespeicherten Daten ein neues QueryDef-Objekt erstellen (s. Listing 3). Dabei soll der Name der erstellten Abfrage als Funktionswert zurückgeliefert werden. Den Namen der frisch erstellten Abfrage übergibt die Prozedur dann an die Routine AbfrageOeffnen, welche die Abfrage in der Entwurfsansicht öffnen soll. Dazu erhält diese Routine außerdem als zweiten Parameter den Wert acViewDesign.

Private Function AbfrageErstellen(lngAbfrageID As Long) As String
     Dim db As DAO.Database
     Dim qdf As DAO.QueryDef
     Dim rst As DAO.Recordset
     Dim strEigenschaften() As String
     Dim i As Integer
     Dim varWert As Variant
     Dim prp As DAO.Property
     Dim lngError As Long
     If lngAbfrageID = 0 Then
         Exit Function
     End If
     Set db = CurrentDb
     Set rst = db.OpenRecordset("SELECT * FROM tblAbfragen WHERE AbfrageID = " & lngAbfrageID, dbOpenDynaset)
     strAktuelleAbfrage = "qryTemp_" & rst!Abfragebezeichnung
     On Error Resume Next
     db.QueryDefs.Delete strAktuelleAbfrage
     On Error GoTo 0
     Set qdf = db.CreateQueryDef(strAktuelleAbfrage, rst!AbfrageSQL)
     strEigenschaften = Split(rst!Abfrageeigenschaften, "|")
     For i = LBound(strEigenschaften) To UBound(strEigenschaften)
         varWert = Split(strEigenschaften(i), "=")(1)
         varWert = Replace(varWert, "Wahr", -1)
         varWert = Replace(varWert, "Falsch", 0)
         On Error Resume Next
         qdf.Properties(Split(strEigenschaften(i), "=")(0)) = varWert
         lngError = Err.Number
         On Error GoTo 0
         If lngError = 3270 Then
             Set prp = qdf.CreateProperty(Split(strEigenschaften(i))(0))
             Select Case strEigenschaften(i)
                 Case Else
                     prp.Type = dbText
             End Select
             prp.Value = varWert
             qdf.Properties.Append prp
         End If
     Next i
     AbfrageErstellen = strAktuelleAbfrage
End Function

Listing 3: Erstellen einer Abfrage aus dem in der Tabelle tblAbfragen gespeicherten Daten

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