3.1 Tabellen wieder einbinden |
https://www.donkarl.com?FAQ3.1 | aktualisiert 2014-01-04 |
Problem
Eine MDB enthält eingebundene Tabellen. Ändert sich das Verzeichnis der Herkunfts-DB dieser Tabellen, dann geht die Verbindung verloren.
Lösung
Die einfachste Variante ist natürlich, die eingebundenen Tabellen zu löschen und über den Menüpunkt Datei/Externe Daten/Tabellen verknüpfen neu einzubinden.
Eine weitere Methode ist die Verwendung des Tabellenverknüpfungs-Managers, der im Menü Extras/Datenbank-Dienstprogramme zu finden ist.
Wenn die Wiedereinbindung dynamisch mit VBA passieren soll, gibt's die Möglichkeit, einen Dateidialog zu verwenden und Code wie in:
http://www.mvps.org/access/tables/tbl0009.htm
Eine Komplettlösung samt Dateidialog und auch für mehrere Backends bietet der JStreet Access Relinker: http://www.jstreettech.com/downloads.aspx
Wenn sicher ist, dass sich beide DBs im gleichen Verzeichnis befinden, lässt sich die Wiedereinbindung auch komplett mit VBA automatisieren. Im Autoexec-Makro oder beim Öffnen des ersten Formulares kann z.B. folgender Code aufgerufen werden:
'******* CODE START *******
On Error GoTo MyError
Dim db As DAO.Database
Dim strDaten As String
Dim i as Integer
Set db = CurrentDB()
strDaten = Left(db.Name, Len(db.Name) - Len(Dir(db.Name))) & "DeineDaten.mdb"
For i = 0 To db.TableDefs.Count - 1
If db.TableDefs(i).Connect <> "" Then
If Mid(db.TableDefs(i).Connect, 11) <> strDaten Then
db.TableDefs(i).Connect = ";database=" & strDaten
db.TableDefs(i).RefreshLink
End If
End If
Next i
MyExit:
Exit Sub
MyError:
MsgBox "Bei der Installation ist eine Ausnahme aufgetreten. ", 16, "Ausnahme"
Resume MyExit
'******* CODE ENDE *******
3.2 Autowert zurücksetzen |
https://www.donkarl.com?FAQ3.2 | aktualisiert 2008-12-12 |
Problem
Du möchtest ein Autowert-Feld einer Tabelle wieder auf 1 oder den nächsthöheren freien Wert zurücksetzen, nachdem Datensätze eingegeben und wieder gelöscht wurden.
Lösung
Komprimiere die Datenbank: Menü Extras/Datenbank-Dienstprogramme/Datenbank komprimieren
Das setzt die Autowerte aller Tabellen auf den nächsthöheren freien Wert zurück.
In Versionen >=A00 funktioniert das oft nicht mehr. s. http://support.microsoft.com/?kbid=287756
Die dort vorgestellten Lösungen sind allerdings unnötig kompliziert, denn ab JET4, d.h. ab A00, kann man Startwert und Schrittweite (Seed und Increment) eines Autowertfeldes per SQL einstellen:
ALTER TABLE Tabelle ALTER COLUMN Feld COUNTER(1,1)
Die erste Zahl setzt den Startwert auf 1 zurück, die zweite stellt die Schrittweite auf 1 ein (kann man auch weglassen, inkl. Komma).
Autowerte sind übrigens für interne Zwecke gedacht, z.B. als Schlüssel- und Beziehungsfelder, nicht für die Erzeugung schöner Nummern. Die sollte man per Programmierung, über Standardwerte oder zu Fuß erzeugen.
3.3 Funktion im Standardwert |
https://www.donkarl.com?FAQ3.3 |
Problem
Du möchtest als Standardwert eines Tabellenfeldes eine Funktion wie z.B. AktuellerBenutzer (bzw. CurrentUser()) oder eine selbst erstellte VBA-Funktion oder einen Bezug auf ein Formularfeld etc. verwenden. Entweder wird die Funktion in einen Text umgewandelt (Access fügt " " drumherum) oder es kommt eine Fehlermeldung: Standardwert wird nicht erkannt, unbekannte Funktion o.ä.
Ursache
Im Standardwert eines Tabellenfeldes sind benutzerdefinierte Funktionen oder Aggregatfunktionen von Access (DomWert etc.) sowie best. Funktionen wie CurrentUser oder Eval nicht möglich. Ebenso funktionieren Verweise auf Access-Objekte nicht.
Lösung
Verwende Formulare. Nur bei Steuerelementen von Formularen sind o.a. Funktionen bzw. Bezüge als Standardwert möglich.
3.4 Prüfen ob Tabelle vorhanden |
https://www.donkarl.com?FAQ3.4 |
Problem
Du möchtest automatisiert prüfen, ob eine bestimmte Tabelle bereits in der DB vorhanden ist.
Lösung
Dafür gibt es mehrere Varianten. Man kann z.B. den Fehler abfangen, der bei Aufruf einer nicht vorhandenen Tabelle erzeugt wird. Sauberer ist aber ein Überprüfen der TableDefs-Auflistung. Dazu kannst du folgende Funktion in ein Standardmodul kopieren:
Function fctTableExists(strTableName As String) As Boolean
Dim db As DAO.Database
Dim tdf As DAO.TableDef
Set db = CurrentDb
For Each tdf In db.TableDefs
If tdf.Name = strTableName Then fctTableExists = True: Exit For
Next
End Function
Eine andere Methode ist die Verwendung der Systemtabelle MSysObjects:
Function fctTableExists(strTableName As String) As Boolean
If DCount("*", "MSysObjects", "Name='" & strTableName & "'") Then fctTableExists = True
End Function
Aufruf in beiden Fällen dann irgendwo in der DB mit:
fctTableExists("DeinTabellenname")
3.5 Datentyp eines Feldes per Code ändern |
https://www.donkarl.com?FAQ3.5 |
Problem
Du möchtest programmatorisch den Typ oder die Größe eines Feldes ändern.
Lösung
Vor der Version A00 ging das nur mit umständlicher Programmierung. Beispiel für das mögliche Vorgehen beim Ändern des Typs eines Feldes in ein Textfeld bzw. Ändern der Feldgröße eines Textfeldes:
'************ CODE START ************
Dim db As DAO.Database
'falls sich die Tabelle in der aktuellen DB befindet
Set db = CurrentDb
'falls sich die Tabelle in einer anderen DB als der aktuellen befindet z.B. bei Frontend/Backend-Aufteilung
'Set db = DBEngine.Workspaces(0).OpenDatabase("c:\Pfad\Andere.mdb")
'ursprüngliches Feld umbenennen
db.TableDefs("MeineTabelle").Fields("MeinFeld").Name = "AltesFeld"
'neues Feld mit gewünschten Einstellungen erzeugen
db.Execute "ALTER TABLE MeineTabelle ADD COLUMN MeinFeld VARCHAR(100)"
'alte Werte übernehmen
db.Execute "UPDATE MeineTabelle SET MeinFeld = AltesFeld"
'altes Feld löschen
db.Execute "ALTER TABLE MeineTabelle DROP COLUMN AltesFeld"
'************ CODE ENDE ************
Seit JET4, d.h. ab Version A00, ist dieses komplizierte Vorgehen nicht mehr nötig. Der ALTER TABLE-Befehl von Access-SQL wurde um ALTER COLUMN erweitert (s. <F1>). Für die gleiche Aufgabe reicht daher (mit obiger db-Variable) die Zeile:
db.Execute "ALTER TABLE MeineTabelle ALTER COLUMN MeinFeld VARCHAR(100)"
3.6 Datensatz zu groß |
https://www.donkarl.com?FAQ3.6 |
Problem
Beim Versuch, einen Datensatz zu speichern, erscheint der Fehler 3047 "Datensatz zu groß."
Ursache
Ein Datensatz einer Access-(d.h. Jet-)Tabelle ist mit ca. 2000 Zeichen begrenzt. In Versionen >=A00 erhöht sich dieses Limit auf ca. 4000 Zeichen, wenn bei Textfeldern die Eigenschaft Unicode-Kompression auf Ja eingestellt ist.
Dabei kommt es nie auf die Eigenschaft Feldgröße an, sondern auf die tatsächlich enthaltenen Zeichen.
Lösung
Memofelder werden bei dieser Begrenzung nicht eingerechnet, weil sie anders verwaltet werden. Du kannst also evtl. die Grenze umgehen, wenn du einige der (größeren) Textfelder in Memos umwandelst.
Ansonsten kannst du die Tabelle auch noch in mehrere gleichartige Tabellen aufteilen und zwischen diesen Tabellen eine 1:1-Beziehung setzen.
3.11 Laufende Nummer/Summe in Abfragen |
https://www.donkarl.com?FAQ3.11 | aktualisiert 2021-10-25 |
Problem
Du möchtest in einer Abfrage ein Feld mit einer fortlaufenden Nummer oder fortlaufenden Summe haben.
Lösung
Laufende Nummer
Du kannst innerhalb deiner Abfrage eine Unterabfrage zum Erzeugen eines Zählerfeldes verwenden. Dazu gibst du in einer eigenen Spalte einen SQL-Text wie den folgenden ein:
LaufNummer: (Select Count (*) FROM [Artikel] as Temp WHERE [Temp].[Id] < [Artikel].[Id])+1
Das Beispiel zeigt, wie man für eine Tabelle "Artikel" mit einem eindeutigen Schlüsselfeld "Id" in der Abfrage ein Zählerfeld erzeugt. Wenn es in deiner Abfrage einschränkende Kriterien gibt oder Du nach anderen Kriterien sortiert hast, wird der SQL-Ausdruck etwas komplizierter:
mein deutscher KB-Artikel dazu im Webarchiv
Eine Alternative zu der Lösung mit der Unterabfrage ist, die laufende Nummer mit DCount (DomAnzahl) zu erstellen:
LaufNummer: DomAnzahl("Id";"Artikel";"Id<" & [Id])+1
Da Unterabfragen mit JET nicht gerade Boliden sind, kann diese Version trotz der oft verpönten Domänenaggregatfunktion bei großen Datenmengen durchaus schneller sein. Also, am besten testen.
Laufende Summe
geht z.B. mithilfe der DSum-Funktion:
Laufende Summe in Abfragen
Gruppierte Laufende Summe in Abfragen
3.12 Geburtstagsliste erstellen |
https://www.donkarl.com?FAQ3.12 | aktualisiert 2021-10-25 |
Problem
Du möchtest eine Abfrage erstellen, um auf Basis eines Geburtsdatum-Feldes zu ermitteln, wer von den Leuten in deiner Tabelle z.B. in den nächsten 10 Tagen Geburtstag haben wird.
Lösung
Du kannst in einer Abfrage in einer neuen Spalte die Geburtstage ins aktuelle Jahr transferieren:
GebHeuer: DatAdd("jjjj";nz(DatDiff("jjjj";[Geburtsdatum];Datum()));[Geburtsdatum])
Dann schreibst du noch als Kriterium:
Zwischen Datum() Und Datum()+10
Diese Variante berücksichtigt natürlich nur Geburtstage im heurigen Jahr. Falls die Sache auch über den Jahreswechsel funktionieren soll, kannst du noch ein zweites berechnetes Feld erzeugen:
GebFolgeJahr: DatAdd("jjjj";nz(DatDiff("jjjj";[Geburtsdatum];Datum()))+1;[Geburtsdatum])
Bei diesem Feld das gleiche Kriterium wie oben verwenden, aber eine Zeile tiefer, also als Oder-Kriterium.
s.a. Alter ermitteln und mein deutscher KB-Artikel dazu im Webarchiv
3.13 Datumskriterium funktioniert nicht |
https://www.donkarl.com?FAQ3.13 |
Problem
Du verwendest in einer Abfrage als Kriterium einen Ausdruck wie =Datum()
und keiner der Datensätze, die dem Kriterium entsprechen sollten, wird zurückgegeben.
Ursache
Die häufigste Ursache ist, dass im Tabellenfeld auch eine Zeit >00:00 Uhr mit gespeichert ist. Wie die Anzeige formatiert ist, ist dann egal, die Zeit wird immer mitgeführt. Das Problem wird meistens dadurch verursacht, dass das Feld irgendwo mit =Now
bzw. =Jetzt()
gefüllt wurde. Wenn man dieses Feld denn mit =Datum()
vergleicht, so wird es immer mit 00:00 Uhr des aktuellen Tages verglichen.
Lösung
Verwende zum automatisierten Füllen von Datumsfeldern, die keine Zeit (bzw. 00:00) enthalten sollen, die Funktion Date statt der Funktion Now.
oder
Verwende für alle Datensätze mit heutigem Datum - unabhängig von der Uhrzeit - in der Abfrage als Kriterium:
>= Datum() UND < Datum()+1
oder
Verwende in der Abfrage beim Feld: Format(DeinFeld;"ttmmjjjj")
und im Kriterium entsprechend: Format(Datum();"ttmmjjjj")
3.14 Kriterium soll alle Datensätze bringen |
https://www.donkarl.com?FAQ3.14 | aktualisiert 2021-10-25 |
Problem
Du möchtest in einer Abfrage bei einem best. Feld ein Kriterium verwenden z.B. einen Bezug auf ein Formularfeld oder einen Parameter. Normalerweise sollen die Datensätze entsprechend diesem Wert gefiltert werden.
Wenn das Kriterium aber leer ist oder einen bestimmten Wert hat (z.B. "*" oder "Alle") dann sollen alle Datensätze der Abfrage erscheinen.
Lösung
Du kannst als Kriterium schreiben:
für ein Steuerelement in einem Formular
[Forms]![DeinFormular]![DeinFeld] Oder [Forms]![DeinFormular]![DeinFeld] Ist Null
für einen leeren Parameter
[DeinParameter] Oder [DeinParameter] Ist Null
für den * (Stern, Asterisk) aus einem Formularfeld
[Forms]![DeinFormular]![DeinFeld] Oder [Forms]![DeinFormular]![DeinFeld] = "*"
bei einem Parameter "Alle"
[DeinParameter] Oder [DeinParameter] = "Alle"
usw.
Wenn der Oder-Teil in den Beispielen Wahr ist, dann werden alle Datensätze der Abfrage zurückgeliefert. Andernfalls werden die Datensätze ganz normal durch den Wert im ersten Teil des Kriterienausdrucks gefiltert.
Diese Methode funktioniert nur, wenn sie für wenige Spalten einer Abfrage angewandt wird. Bei mehr als ein paar Kriterien dieser Art hat Access/JET meist Probleme, die Abfrage auszuführen. In diesem Fall ist es besser, einen SQL-Text per Code zu basteln, der nur die Kriterien beinhaltet, die einen Wert haben. Das verbessert sowohl das SQL-Statement als auch die Performance.
mein deutscher KB-Artikel dazu im Webarchiv
3.15 Variablen in Abfragen verwenden |
https://www.donkarl.com?FAQ3.15 |
Problem
Du möchtest eine globale Variable in einer Abfrage verwenden, z.B. als Kriterium oder Teil eines berechneten Feldes. Wenn du das versuchst, wird die Variable nicht erkannt sondern als String oder Parameter angesehen.
Lösung
Du kannst eine Variable nicht direkt in einer Abfrage verwenden. Erzeuge stattdessen in einem Standardmodul eine Funktion, die nichts anderes tut, als den Wert der Variablen zurückzugeben. z.B.
Public Function fctSendVar() ' hier evtl. noch den Typ angeben
fctSendVar = DeineGlobaleVariable
End Function
In der Abfrage schreibst du dann statt der Variablen den Namen der Funktion inkl. Klammern.
Auf die gleiche Weise kannst du übrigens auch den Wert einer Variablen als Steuerelementinhalt in Formularen oder Berichten verwenden.
3.16 Datensätze aus A, die nicht in B sind |
https://www.donkarl.com?FAQ3.16 |
Problem
Du hast 2 Tabellen "A" und "B" und möchtest herausfinden, welche Datensätze sich in A befinden, aber nicht in B.
Lösung
Verknüpfe die Tabellen im Abfrageentwurf über dafür geeignete Felder (Primärschlüssel und Fremdschlüssel o.ä.) und mach einen Doppelklick auf die Verknüpfungslinie. Dort stellst du die zweite Variante an Beziehung ein, also jene, die alle DS aus Tabelle A bringt und nur die aus Tabelle B usw… Hol den Stern aus Tabelle A als Feld in die Abfrage und das Primärschlüsselfeld aus Tabelle B als weiteres Feld. Dieses Feld brauchst du nicht anzeigen zu lassen, aber als Kritierum schreibst du: Ist Null
Das SQL-Statement sieht dann ca. so aus:
SELECT A.*
FROM A LEFT JOIN B ON A.Id = B.Id
WHERE B.Id Is Null
3.17 Zufällige Reihenfolge der Datensätze erreichen |
https://www.donkarl.com?FAQ3.17 |
Problem
Du möchtest in einer Abfrage erreichen, dass sich die Reihenfolge der Datensätze nach Zufallsprinzip bei jedem Aufruf ändert.
Lösung
Für die Erzeugung von Zufallszahlen bietet VBA die Randomize-Funktion Rnd() (in Abfragen heißt sie ZZG).
Um eine zufällige Sortierung zu erreichen, kannst du ein neues Feld erzeugen und darin ein Autowert-Feld verwenden, das in der zugrundeliegenden Tabelle hoffentlich existiert (falls nicht, dann erzeugen).
Der Ausdruck für das neue Feld lautet dann: Rnd(MeinAutowertFeld)
Nach diesem Feld sortieren lassen.
3.18 Parameter in Kreuztabellenabfragen |
https://www.donkarl.com?FAQ3.18 |
Problem
Du verwendest in einer Kreuztabellenabfrage einen Parameter, den du über ein Parameterfenster eingeben möchtest oder z.B. einen Formularbezug als Kriterium, der bei anderen Arten von Abfragen tadellos funktioniert. Bei der Kreuztabellenabfrage aber erscheint die Fehlermeldung:
"Das Microsoft Jet-Datenbankmodul erkennt <Name> nicht als gültigen Feldnamen oder Ausdruck. (Fehler 3070)"
Ursache
Bei einer Kreuztabellenabfrage werden die Spaltennamen dynamisch erzeugt. Daher weiß Access nicht, ob es sich um einen Parameter oder einen Feldbezug handelt. Erst später, beim Versuch von JET, die Spalte an ein Tabellenfeld zu binden, kommt es zu dem Fehler.
Lösung
Trage den Parameter, Formularbezug etc. und den Datentyp zusätzlich in die Parameterliste der Kreuztabellenabfrage ein. Im Abfrageentwurf Menü Abfrage/Parameter. Damit kennt Access Name und Typ des Parameters und geht von Anfang an richtig damit um.
3.19 Groß- und Kleinschreibung unterscheiden |
https://www.donkarl.com?FAQ3.19 | aktualisiert 2005-11-27 |
Problem
Du möchtest bei einem Kriterium oder bei einer Verknüpfung unterscheiden, ob ein Buchstabe groß oder klein geschrieben ist. Access interessiert das aber nicht.
Ursache
JET, die Standard-Datenbank-Engine von Access, unterscheidet nicht zwischen Groß- und Kleinschreibung, ist also nicht "case-sensitive".
Lösung
Du kannst die VBA-Funktion StrComp() verwenden, um zwischen Groß- und Kleinschreibung (übrigens auch zwischen "ss" und "ß") zu unterscheiden. Angenommen, FeldX soll mit einem Kriterium "abc" verglichen werden. Als Ergebnis soll nur diese klein geschriebene Variante kommen, nicht "ABC", "Abc", "aBc" etc. Dafür legst du ein neues Abfragefeld an, in dem folgender Ausdruck steht:
StrComp([FeldX];"abc";0)
Die 0 hinten (für den Parameter Compare) sorgt für den nötigen binären Vergleich. Nur wenn die verglichenen Werte in ihrer Schreibweise exakt übereinstimmen, liefert StrComp() den Rückgabewert 0 (s. <F1> zu "StrComp"). Du kannst daher die Treffer rausfiltern, indem du in der Kriterienzeile des Feldes schreibst: 0
s.a. http://support.microsoft.com/?kbid=209674
MIt dem gleichen Vorgehen kann man bei einer Verknüpfung zwischen Groß- und Kleinschreibung in den verknüpfenden Feldern unterscheiden, indem man eben wie o.a. ein zusätzliches Feld mit dem binären StrComp-Vergleich für die beiden Verknüpfungsfelder einsetzt. In SQL wäre das z.B.:
SELECT *
FROM Tabelle1 INNER JOIN Tabelle2 ON Tabelle1.FeldX = Tabelle2.FeldX
WHERE StrComp(Tabelle1.FeldX, Tabelle2.FeldX, 0) = 0
s.a. http://support.microsoft.com/?kbid=244693