 | Unser Angebot für Sie! Lesen Sie diesen Beitrag und 500 andere sofort im Onlinearchiv, und erhalten Sie alle zwei Monate brandheißes Access-Know-how auf 72 gedruckten Seiten! Plus attraktive Präsente, zum Beispiel das bald erscheinende Buch 'Access 2010 - Das Grundlagenbuch für Entwickler'! |
| | | | | |
Zusammenfassung
Erfahren Sie, wie Sie Datenbankschemata auslesen und SQL-Anweisungen zum Nachbauen der Tabellen einer Datenbank erstellen.
Techniken
VBA, DAO, SQL
Voraussetzungen
Access 2000 und höher
Beispieldateien
SQLDump.mdb
Shortlink
www.access-im-unternehmen.de/698
SQL-Datenbankschema auslesen
André Minhorst, Duisburg
Unter dem SQL-Datenbankschema verstehen wir eine Reihe von SQL-Anweisungen, mit denen Sie Tabellen oder auch eine komplette Datenbank erstellen können. Dazu gehören beispielsweise die CREATE TABLE- oder CREATE INDEX-Anweisungen. Da man diese Befehle eher selten einsetzt, kann eine Lösung zum automatischen Erstellen des Schemas einer oder mehrerer Tabellen nicht schaden. Der vorliegende Beitrag zeigt, wie es funktioniert.
Access macht es dem Entwickler leicht: Mit der Tabellenentwurfs-Ansicht, dem Indizes-Fenster und dem Beziehungen-Fenster bietet es uns drei Möglichkeiten, Tabellen und deren Beziehungen ganz einfach anzulegen. Im Tabellenentwurfsfenster erfassen wir die grundlegenden Daten wie die Felder mit ihren Datentypen und weitere Eigenschaften, im Indizes-Fenster verwalten wir einfache und zusammengesetzte Indizes und das Beziehungen-Fenster dient schließlich dazu, die Beziehungen zwischen den Tabellen anzulegen.
Im Hintergrund rödelt Access ganz ordentlich und stellt die SQL-Anweisungen zusammen, welche die von uns angelegten Tabellen und Felder und auch Änderungen an bestehenden Elementen zur Ausführung an die JET-Engine weitergeben.
Solche Ausdrücke können Sie mit der Lösung des vorliegenden Beitrags selbst erstellen: Sie brauchen einfach nur das Formular aus Abb. 1 zu öffnen und die gewünschte Tabelle auszuwählen, schon erscheinen die SQL-Ausdrücke zum Erstellen dieser Tabellen mit den gebräuchlichsten Eigenschaften im Formular.
Abb. 1: Durch Auswählen einer Tabelle erhalten Sie den SQL-Ausdruck, um diese zu erstellen.
Grundlagen
SQL verwenden Sie ja normalerweise, um Abfragen für die Auswahl von Daten zu definieren oder auch zum Ausführen von Aktionsabfragen. Dieser Teil von SQL nennt sich Data Manipulation Language (DML). Für den vorliegenden Beitrag brauchen wir allerdings eine andere SQL-Teilmenge, nämlich die Data Definition Language (DDL). Sie enthält Befehle wie die oben bereits erwähnten CREATE TABLE...- oder CREATE INDEX...-Anweisungen. Mit ein wenig Einarbeitung können Sie mit diesen Befehlen Tabellen und Beziehungen sicher genauso schnell, wenn nicht noch schneller als mit den Elementen der Benutzeroberfläche erstellen.
Da die meisten von uns bequem sind, wird das aber kaum jemand tun. Dennoch kommt es vor, dass man eine oder mehrere SQL-Anweisungen zum Anlegen von Tabellen benötigt - beispielsweise, wenn Sie eine Tabelle per VBA-Code erstellen möchten. Dabei kann es sich um eine temporäre Tabelle handeln, die nur kurzzeitig benötigt und dann wieder gelöscht wird, zum Beispiel um zu importierende Daten vor dem Weiterverarbeiten zwischenzulagern. Oder Sie bauen einen Assistenten oder ein Tool, das per Mausklick Tabellen in Ihren Datenbanken anlegen soll. Dies ist zum Beispiel äußerst praktisch, wenn Ihre Datenbankanwendung immer wiederkehrerende Tabellen wie etwa eine Anreden-Tabelle enthalten soll.
Ziel der nachfolgend vorgestellten Prozeduren ist es, auf Basis bestehender Tabellen SQL-Abfragen zu erzeugen, mit denen Sie die Tabellen nachbauen können. Für eine einfache Tabelle mit einem Primärschlüsselfeld namens AnredeID und einem weiteren Textfeld namens Anrede mit einer Feldgröße von 255 Zeichen benötigen Sie beispielsweise die folgenden beiden Zeilen:
CREATE TABLE tblAnreden([AnredeID] COUNTER, [Anrede] CHAR(255));
CREATE UNIQUE INDEX [PrimaryKey] ON [tblAnreden]([AnredeID]) WITH PRIMARY DISALLOW NULL;
Das Ganze ließe sich auch mit einer einzigen Zeile erledigen, und zwar so:
CREATE TABLE tblAnreden([AnredeID] COUNTER CONSTRAINT PrimaryKey PRIMARY KEY, [Anrede] CHAR(255));
Warum wir in der nachfolgend beschriebenen Lösung die erste Variante verwenden, erfahren Sie weiter unten. Eine ganze Menge Informationen zu den Befehlen der Data Definition Language erhalten Sie im Beitrag Datenbanken und Tabellen per SQL anpassen (Shortlink 609).
DAO als Werkzeug zum Auslesen von Tabellen-, Feld- und Beziehungsinformationen
Bevor wir uns um das Erstellen der SQL-Anweisungen kümmern können, brauchen wir Möglichkeiten, die Struktur der Tabellen, Felder und Beziehungen auszulesen.
Wie aber soll dies gelingen - welche Bibliothek bietet Zugriff auf den Entwurf einer Datenbank? Es handelt sich um die DAO-Bibliothek, die Sie für den Zugriff auf die Daten einer Datenbank sicher schon Hunderte Male genutzt haben. Die für uns interessante Struktur basiert auf dem Database-Objekt, das Sie normalerweise als Ausgangspunkt für die OpenRecordset- oder die Execute-Methode verwenden. In diesem Fall greifen wir auf dessen TableDefs-Auflistung zu, die den Zugriff auf alle Tabellen der Datenbank erlaubt. Ein TableDef-Objekt wiederum enthält unter anderem die Fields-Auflistung, die wiederum die Field-Objekte und somit die Informationen über die Felder der entsprechenden Tabelle liefert. Außerdem stellt das TableDef-Objekt die Indexes-Auflistung bereit, mit der wir die Indizes der angegebenen Tabelle auslesen können.
Etwas weiter oben stellt das Database-Objekt neben der TableDefs-Auflistung auch noch die Relations-Auflistung bereit. Diese wiederum enthält die Relation-Objekte mit allen Informationen zu den im Beziehungen-Fenster angelegten Beziehungen.
Tabellendefinition auslesen
Den Start macht die Funktion GetCreateTableString (s. Listing 1). Sie erwartet ein TableDef-Objekt mit einem Verweis auf die auszuwertende Tabelle und liefert eine Zeichenkette mit dem SQL-Ausdruck zum Erstellen der entsprechenden Tabelle zurück. Diese Zeichenkette enthält die CREATE TABLE-Anweisung mit der Angabe des Namens der zu erstellenden Tabelle, die Feldliste sowie eventuell nötige CONSTRAINT-Elemente zur Definition von Fremdschlüsselfeldern.
Public Function GetCreateTableString(tdf As DAO.TableDef) As String
Dim db As DAO.Database
Dim fld As DAO.Field
Dim idx As DAO.Index
Dim rel As DAO.Relation
Dim strTable As String
Dim strSQL As String
Dim strSQLField As String
Dim strFieldType As String
Dim strConstraints As String
Set db = CurrentDb
strTable = tdf.Name
strSQL = "CREATE TABLE " & strTable & "("
For Each fld In tdf.Fields
strSQLField = strSQLField & "[" & fld.Name & "]"
strFieldType = GetFieldType(fld)
strSQLField = strSQLField & " " & strFieldType & ", "
Next fld
strSQL = strSQL & strSQLField
For Each idx In tdf.Indexes
If idx.Foreign Then
For Each rel In db.Relations
If rel.ForeignTable = tdf.Name And idx.Fields(0).Name = rel.Fields(0).Name Then
strConstraints = strConstraints & "CONSTRAINT "
strConstraints = strConstraints & "[" & idx.Name & "] FOREIGN KEY ("
strConstraints = strConstraints & "[" & rel.Fields(0).Name & "]"
strConstraints = strConstraints & ") REFERENCES ["
strConstraints = strConstraints & rel.Table & "]"
If (rel.Attributes And dbRelationUpdateCascade) = dbRelationUpdateCascade Then
strConstraints = strConstraints & " ON UPDATE CASCADE"
End If
If (rel.Attributes And dbRelationDeleteCascade) = dbRelationDeleteCascade Then
strConstraints = strConstraints & " ON DELETE CASCADE"
End If
strConstraints = strConstraints & ", "
End If
Next rel
End If
Next idx
strSQL = strSQL & strConstraints
If Right(Trim(strSQL), 1) = "," Then
strSQL = Left(strSQL, Len(Trim(strSQL)) - 1)
End If
strSQL = strSQL & ");"
GetCreateTableString = strSQL
End Function
Um einen solchen Ausdruck zu erstellen, verwendet die Prozedur eine String-Variable namens strSQL, die nach und nach erweitert wird. Gleich zu Beginn schreibt die Prozedur den Ausdruck CREATE TABLE gefolgt von dem im Parameter angegebenen Tabellennamen und einer öffnenden Klammer in diese Variable. Die öffnende Klammer gibt den Startschuss für die folgende Feldliste ab. Diese stellt die Prozedur innerhalb einer For Each-Schleife zusammen, die alle Field-Elemente des TableDef-Objekts auf Basis der gewünschten Tabelle durchläuft. Darin bedienen wir uns einer Hilfsvariablen namens strSQLField, welche die Feldliste aufnimmt. Zu jedem Element der Feldliste gehört der Name der Tabelle, der vorsichtshalber in eckigen Klammern eingefasst wird - auf diese Weise verhindern wir, dass Feldnamen mit Sonderzeichen zu Fehlern beim Ausführen der SQL-Anweisungen führen. Hinter dem Feldnamen folgt der Datentyp. Diesen ermittelt eine separate Funktion namens GetFieldType (s. Listing 2) - mehr dazu im Anschluss an die Beschreibung der aktuellen Funktion. Vorerst reicht uns die Information, dass GetFieldType beispielsweise für ein Währungsfeld den Ausdruck MONEY zurückliefert.
Public Function GetFieldType(fld As DAO.Field) As String
Dim strFieldType As String
Select Case fld.Type
Case dbLong
If (fld.Attributes And dbAutoIncrField) = 0 Then
strFieldType = "INTEGER"
Else
strFieldType = "COUNTER"
End If
Case dbText
strFieldType = "TEXT"
strFieldType = strFieldType & "(" & fld.Size & ")"
Case dbCurrency
strFieldType = "MONEY"
Case dbInteger
strFieldType = "SMALLINT"
Case dbBoolean
strFieldType = "BIT"
Case dbSingle
strFieldType = "REAL"
Case dbDate
strFieldType = "DATETIME"
Case dbLongBinary
strFieldType = "IMAGE"
Case dbMemo
strFieldType = "LONGTEXT"
Case dbByte
strFieldType = "BYTE"
Case dbSingle
strFieldType = "SINGLE"
Case dbDouble
strFieldType = "DOUBLE"
Case dbBinary
strFieldType = "BINARY"
Case dbLongBinary
strFieldType = "OLE OBJECT"
Case dbGUID
strFieldType = "GUID"
Case Else
strFieldType = fld.Type
Stop
Debug.Print fld.Type
End Select
GetFieldType = strFieldType
End Function
Mit dem Feldnamen und dem Datentyp stellt unsere For Each-Schleife also Ausdrücke der Form
[AnredeID] COUNTER, [Anrede] CHAR(255),
zusammen. Um das angehängte Komma kümmert sich die Prozedur später.
Der folgende Block enthält eine weitere For Each-Schleife, welche diesmal alle Index-Elemente der aktuellen Tabelle durchläuft. Das Index-Element enthält eine Reihe verschiedener Eigenschaften, welche die Art des Indexes festlegen: Foreign, IgnoreNulls, Primary, Required und Unique. Die Funktion GetCreateTableString kümmert sich nur um die Foreign-Indizes - alle übrigen werden in einer separaten Funktion behandelt.
Hat die Foreign-Eigenschaft eines Index-Elements den Wert True, handelt es sich um einen Fremdschlüsselindex, für den es eine Beziehung zu einer anderen Tabelle gibt. Die Beziehungen einer Datenbank werden über die Relations-Auflistung des Database-Objekts verfügbar gemacht. Wenn die Funktion GetCreateTableString eine Tabelle mit einem Foreign-Index entdeckt, durchsucht sie die Relations-Auflistung nach einem Relation-Element, dessen Eigenschaft ForeignTable den Namen der aktuellen Tabelle enthält und deren Feld mit dem Feld des aktuellen Indexes übereinstimmt. Hat sie ein passendes Relation-Element gefunden, stellen die folgenden Zeilen zum Beispiel diesen Ausdruck zusammen:
CONSTRAINT [tblKategorientblArtikel] FOREIGN KEY ([KategorieID]) REFERENCES [tblKategorien] ON UPDATE CASCADE ON DELETE CASCADE,
Ob der Ausdruck ON UPDATE CASCADE (Aktualisierungsweitergabe) oder ON DELETE CASCADE (Löschweitergabe) eingefügt wird, hängt davon ab, ob der Benutzer die entsprechenden Eigenschaften im Dialog Beziehungen bearbeiten eingestellt hat. Dies ist zum Beispiel in Abb. 2 der Fall.
Abb. 2: Beziehungseigenschaften mit Lösch- und Aktualisierungsweitergabe
Aus den Eigenschaften des Relation-Objekts bekommt man dies nur über Umwege heraus. Die benötigten Informationen stecken in der Eigenschaft Attributes, die - wie die Pluralform schon andeutet - die Werte mehrerer Eigenschaften speichern kann. Bei diesen Eigenschaften kann es sich freilich nur um Boolean-Eigenschaften auf Bit-Ebene handeln: Ob eine Eigenschaft den Wert True oder False aufweist, hängt nämlich davon ab, ob ein die Eigenschaft repräsentierender Zahlenwert im Bit-Wert der Eigenschaft Attributes enthalten ist. Haben alle Eigenschaften den Wert False, ist Attributes gleich 0. Hat eine der Eigenschaften den Wert True, wird ein entsprechender Zahlenwert zum bestehenden Eigenschaftswert von Attributes hinzuaddiert. Wie kommt man aber nun an diese Zahlenwerte? Hier hilft ein Blick in den Objektkatalog weiter (s. Abb. 3). Wenn Sie hier auf eine der db...-Konstanten klicken, finden Sie im unteren Bereich den entsprechenden Zahlenwert.
Abb. 3: Der Objektkatalog liefert die möglichen Eigenschaftswerte für die Eigenschaft Attributes des Relation-Objekts.
Damit man aus mehreren addierten Zahlenwerten ablesen kann, ob der Zahlenwert für eine bestimmte Eigenschaft enthalten ist, müssen die Zahlenwerte Zweierpotenzen sein (also 0, 1, 2, 4, 8 und so weiter). In diesem Fall entspricht dbRelationDeleteCascade dem Wert 4096 und dbRelationUpdateCascade dem Wert 256. Unabhängig von den übrigen möglichen Eigenschaftswerten enthält Attributes nun den Wert 4096, wenn Löschweitergabe aktiviert ist, den Wert 256, wenn nur Aktualisierungsweitergabe aktiviert ist, und die Summe aus beiden, also 4352, wenn der Benutzer beide Optionen im Dialog Beziehungen bearbeiten aktiviert hat. Im Code können wir mit dem And-Operator ganz einfach prüfen, ob eine der interessierenden Eigenschaften gesetzt ist. Der folgende Ausdruck liefert beispielsweise True zurück, wenn dbRelationDeleteCascade gesetzt ist:
(rel.Attributes And dbRelationUpdateCascade) = dbRelationUpdateCascade
Wie dies technisch funktioniert, lesen Sie im Beitrag Rund um Binärzahlen (Shortlink 556). Nach dem Zusammensetzen der Feldliste und der Foreign-Constraints-Liste entfernt die Funktion noch das Komma hinter dem letzten Element, schließt die Klammer und gibt den resultierenden Ausdruck als Funktionswert zurück.
Datentypen ermitteln
Kommen wir zur Funktion GetFieldType aus Listing 2 zurück. Diese soll einen Ausdruck ermitteln, der auf Basis der Type-Eigenschaft des Field-Objekts einen SQL-kompatiblen Datentyp zurückliefert.
Public Function GetCreateIndexString(tdf As DAO.TableDef, _
idx As DAO.Index) As String
Dim fld As DAO.Field
Dim strIndex As String
Dim strFields As String
Dim strWith As String
Dim strGUID As String
strIndex = strIndex & "CREATE "
If idx.Unique Then
strIndex = strIndex & "UNIQUE "
End If
strIndex = strIndex & "INDEX "
If idx.Foreign Then
strGUID = CreateGUID
End If
strIndex = strIndex & "[" & strGUID & idx.Name & "]"
strIndex = strIndex & " ON "
strIndex = strIndex & "[" & tdf.Name & "]"
strFields = ""
For Each fld In idx.Fields
strFields = strFields & "[" & fld.Name & "], "
Next fld
strFields = Left(strFields, Len(strFields) - 2)
strIndex = strIndex & "("
strIndex = strIndex & strFields
strIndex = strIndex & ")"
strWith = ""
If idx.Primary Then
strWith = strWith & "PRIMARY "
End If
If idx.Required Then
strWith = strWith & "DISALLOW NULL "
End If
If idx.IgnoreNulls Then
strWith = strWith & "IGNORE NULL "
End If
If Len(strWith) > 0 Then
strIndex = strIndex & " WITH " & Trim(strWith)
End If
strIndex = strIndex & ";"
GetCreateIndexString = strIndex
End Function
Die Type-Eigenschaft liefert uns aber erstmal nur Zahlenwerte. Wie sollen wir von denen auf den benötigten SQL-Datentyp schließen? Hier hilft wiederum der Objektkatalog weiter: Er liefert mit der Enumeration DataTypeEnum eine Liste aller möglichen DAO-Datentypen (s. Abb. 4). Diese müssen wir nun noch mit den unter SQL verwendeten Datentypen abgleichen, wobei die VBA-Online-Hilfe hilfreich ist - unter Access 2007 kommt man hier etwa mit den Schlüsselwörtern SQL und Datentypen zu einer Übersicht der SQL-Datentypen.
Abb. 4: Auch für die Datentypen liefert der Objektkatalog die entsprechenden Konstanten.
Das Ergebnis dieser Recherche finden Sie in angereicherter Form eben in der Funktion GetFieldType. Diese liefert im einfachen Fall einfach das zum Type-Wert passende SQL-Schlüsselwort zurück. In manchen Fällen brauchen wir weitere Informationen:
- Beim Long-Datentyp handelt es sich oft um Autowerte. Wenn die Attributes-Eigenschaft des Field-Objekts das Attribut dbAutoIncrField enthält, ist dies der Fall. Dann soll die Funktion als Datentyp COUNTER statt INTEGER zurückliefern.
- Für Textfelder wird der Datentyp TEXT mit der Angabe der Feldgröße in Klammern zurückgeliefert. Die Feldgröße liefert die Size-Eigenschaft des Field-Objekts.
Indizes definieren
Nachdem die Funktion GetCreateTableString den eigentlichen Tabellenaufbau inklusive Beziehungen liefert, ist die Funktion GetCreateIndexString (s. Listing 3) für die übrigen Indizes verantwortlich. Die Funktion wird für jedes Index-Element der Indexes-Auflistung eines TableDef-Objekts einmal aufgerufen und erwartet das zu untersuchende TableDef- und Index-Element als Parameter.
Auch diese Funktion beginnt das Zusammensetzen des gefragten Ausdrucks mit dem CREATE-Schlüsselwort. Dann fügt sie, wenn es sich bei dem Index um einen eindeutigen Index handelt, das UNIQUE-Schlüsselwort an - in jedem Falle gefolgt von INDEX.
Für den Namen des Index-Elements ergibt sich eine Besonderheit: Offensichtlich bietet SQL nicht die Möglichkeit, einen Primärschlüssel- oder eindeutigen Index gleichzeitig als Fremdschlüssel einzurichten (dies geschieht nicht besonders oft, kann aber vorkommen, wenn man 1:1-Beziehungen einrichtet). Interessanterweise besitzt Access aber intern wohl die Möglichkeit, unter dem gleichen Namen Indizes mit Primär- und Fremdschlüsseleigenschaften gleichzeitig einzurichten. Die vorliegende Lösung erzeugt daraus zunächst einen FOREIGN KEY-Constraint und anschließend in der Funktion GetCreateIndexString einen gegebenenfalls als UNIQUE gekennzeichneten PRIMARY-Constraint. Dieser sollte eigentlich den gleichen Namen wie der FOREIGN KEY-Index haben. Das Anlegen von Indizes mit bereits vergebenen Namen löst allerdings einen Fehler aus, sodass wir dem Namen des betroffenen Indexes eine GUID zur Alleinstellung voranstellen.
Weiter im Code: Das Schlüsselwort ON und der Name der Tabelle ergänzen die CREATE INDEX-Anweisung. Anschließend folgt in Klammern die Feldliste. Schließlich liest die Funktion anhand der Eigenschaften Primary, Required und IgnoreNulls die Eigenschaften des Indexes aus und fügt entsprechende SQL-Schlüsselwörter an den Ausdruck an. Diese entsprechen genau den drei Eigenschaften, die Sie im Indizes-Dialog beim Entwurf einer Tabelle angeben können (s. Abb. 5).
Abb. 5: Eigenschaften eines Indexes im Tabellenentwurf
Einfacher Aufruf
Die Funktion CreateTableString rundet die bislang vorgestellten Funktionen ab (s. Listing 4). Sie erwartet lediglich den Namen der Tabelle, die Sie per SQL-Anweisung reproduzieren möchten, und gibt die benötigten SQL-Ausdrücke zurück. Dazu ruft sie die bisher vorgestellten Funktionen mit den entsprechenden Parametern auf. Das Formular frmDDL, das wir ganz zu Beginn in Abb. 1 vorgestellt haben, macht sich diese Funktion zunutze, indem sie diese nach der Auswahl einer der Tabellen im Kombinationsfeld aufruft und den resultierenden Ausdruck anzeigt. Wenn Sie die Lösung in eigenen Datenbanken einsetzen möchten, brauchen Sie lediglich das Formular frmDDL und die Module mdlSQLDump und mdlGUIDs in die Datenbank zu importieren und das Formular zu starten.
Public Function CreateTableString(strTable As String) As String
Dim db As DAO.Database
Dim idx As DAO.Index
Dim tdf As DAO.TableDef
Dim strSQL As String
Dim strIndex As String
Set db = CurrentDb
Set tdf = db.TableDefs(strTable)
strSQL = GetCreateTableString(tdf)
For Each idx In tdf.Indexes
strIndex = strIndex & GetCreateIndexString(tdf, idx) & vbCrLf
Next idx
CreateTableString = strSQL & vbCrLf & strIndex
End Function
Vorgehensweise beim Ausführen der SQL-Anweisungen
Ausführen können Sie die Anweisungen mit einem Klick auf die Schaltfläche cmdAusfuehren. Dies löst die folgende kleine Routine aus:
Public Sub SQLAusfuehren(strSQL As String)
Dim cnn As ADODB.Connection
Dim strSQLLines() As String
Dim i As Integer
Set cnn = CurrentProject.Connection
strSQLLines = Split(strSQL, vbCrLf)
For i = LBound(strSQLLines) To _
UBound(strSQLLines)
cnn.Execute strSQLLines(i)
Next i
End Sub
Die Routine teilt die aus mehreren durch Zeilenumbrüche getrennten Anweisungen auf und schreibt diese in ein Array, dessen Elemente dann mit der Execute-Methode eines ADODB-Connection-Objekts ausgeführt werden. Der Vorteil gegenüber der gleichnamigen Methode des DAO-Database-Objekts ist, dass Sie per ADODB mehr Möglichkeiten haben - die Lösch- und Aktualisierungsweitergabe etwa lässt sich mit DAO nicht anwenden. Die RefreshDatabaseWindow-Methode des Application-Objekts aktualisiert schließlich die Anzeige im Datenbankfenster. Beachten Sie beim Anlegen von Datenmodellen mit verknüpften Tabellen, dass vor dem Anlegen von Tabellen mit Fremdschlüsselfeldern die Tabellen mit dem entsprechenden Primärschlüsselfeld vorhanden sein müssen. Wenn Sie gleich mehrere Tabellen auf einen Rutsch erstellen möchten, fügen Sie die mit dieser Lösung erzeugten SQL-Anweisungen in der richtigen Reihenfolge aneinander. Achten Sie darauf, dass einzelne Anweisungen durch einen Zeilenumbruch (vbcrlf) voneinander getrennt sein müssen. |