Service: Visual-Basic 6.0 Tipps: Access Datenbank per Code kopieren mit DAO
Eigentlich müsste dieser Tipp lauten: 'Textfeldlänge in einer Access-Datenbank-Tabelle mit DAO ändern'. Das war die ursprüngliche Aufgabe. Das hat sich aber zu einer etwas größeren Geschichte ausgeweitet. Also der Reihe nach:
In der betreffenden Datenbank wurde ein Textfeld als Primärindex verwendet. Es stellte sich heraus, dass die Feldlänge von 28 auf 50 geändert werden muss.
Der einfache Weg, die Änderung mit dem Access-Frontend durchzuführen war versperrt, da die Datenbank bereits bei einer Vielzahl von Nutzern im Einsatz war. Also: Änderung zur Laufzeit beim Programmstart durchführen.
Probleme dabei:
Schlussfolgerung: Vor Änderung der Feldlänge zuerst die Beziehungen entfernen und nach Durchführung der Änderungen wiederherstellen. (Für die Wiederherstellung zuerst die Original-Datenbank kopieren und später als Vorlage verwenden.)
Umständlich genug - aber: Aufgrund eines Fehlers in Access 2000 war es nicht möglich, per DAO alle Beziehungen zu löschen (zumindest nicht für die betreffende Datenbank)!
Was nun? Nach ein wenig Nachdenken: Ganz einfach die Datenbank per DAO vollständig kopieren - also zunächst das Datenbank-Design (Tabellen mit Feldern und die Beziehungen) und dann die Daten.
Nachdem man darüber nachdenkt, ist das Kopieren des Datenbank-Designs eine sehr einfache Sache:
Es wäre eine einfache Sache gewesen. Nur leider: Die entsprechende Code-Implementierung meldet beim Versuch, die erste Beziehung zu kopieren den Fehler 3284, 'Der Index ist bereits vorhanden.'
Es hat etwas Nachdenken und eine Kaffeepause benötigt, um herauszufinden, was das Problem hier ist. Aber schließlich ging dem Programmierer ein Licht auf:
Jede Beziehung wird nicht nur als eigenständiges Relation-Objekt in der Access-Datenbank verwaltet, sondern zusätzlich werden Indices in den betreffenden Tabellendefinitionen verwaltet.
Die endgültige Lösung bestand also darin, beim kopieren der Indices, die den Tabellen zugeordnet sind, die Indices auszusparen, die Bestandteil von Beziehungen sind.
(Einige Anmerkungen dazu weiter unten)
'--------------------------------------------------------------------------------
' Procedure......: UpdateDBReferenceHandling
' Author.........: Ralf Kunsmann
' Date...........: 2007 02 22
' Purpose........: DB tables have a text-field ('RUID') as primary key, having
' 28 characters length. Fields have to be updated to have 50
' chars length.
' For fields are part of releations and DAO is not able to
' redefine field sizes, 1st it was planed remove relations,
' update field sizes and restore relations.
' But this was not feasible because of an error in Jet-Engine
' (sweat!): Relations couldn't be removed. So the only
' solution is to copy the complete database (design and
' contents).
' Parameters.....: Object instance of opened (original) database.
'--------------------------------------------------------------------------------
Public Sub UpdateDBReferenceHandling(db As Database)
Const sPROCEDURENAME As String = "UpdateDBReferenceHandling"
On Error GoTo ErrorUpdateDBReferenceHandling
' Stuff was already done? Exit!
If UpdateDBReferenceHandlingDone(db) Then Exit Sub
' Get names of all relations to a dictionary
Dim dicRel As Dictionary: Set dicRel = New Dictionary
Dim rel As Relation
For Each rel In db.Relations
dicRel.Add rel.Name, rel.Name
Next
Dim sDbPath As String: sDbPath = db.Name
' Create new Access database
Dim sNewDBPath As String: sNewDBPath = Replace(sDbPath, ".mdb", "New.mdb")
If ExistFile(sNewDBPath) Then Kill sNewDBPath
Dim dbNew As Database: Set dbNew = CreateDatabase(sNewDBPath, dbLangGeneral)
' All tables in original database, except access' own management tables ...
Dim td As TableDef, tdNew As TableDef
Dim fld As Field, fldNew As Field
Dim idx As Index, idxNew As Index
For Each td In db.TableDefs
If InStr(td.Name, "MSys") = 0 Then
' Create new table in new database
Set tdNew = New TableDef
tdNew.Name = td.Name
For Each fld In td.Fields
' Append new fields to new table based on fields in original table
Set fldNew = New Field
fldNew.Name = fld.Name
fldNew.Type = fld.Type
If fld.Type = dbText Or fld.Type = dbMemo Then _
fldNew.AllowZeroLength = fld.AllowZeroLength
fldNew.Attributes = fld.Attributes
fldNew.DefaultValue = fld.DefaultValue
fldNew.OrdinalPosition = fld.OrdinalPosition
fldNew.Required = fld.Required
fldNew.Size = IIf(InStr(fld.Name, "RUID") > 0, 50, fld.Size)
fldNew.ValidationRule = fld.ValidationRule
fldNew.ValidationText = fld.ValidationText
tdNew.Fields.Append fldNew
Next
For Each idx In td.Indexes
' Append new indices to new table based on indices in original table.
' Exclude the indices, that are part of the relations.
If Not dicRel.Exists(idx.Name) Then
Set idxNew = New Index
idxNew.Clustered = idx.Clustered
idxNew.Fields = idx.Fields
idxNew.IgnoreNulls = idx.IgnoreNulls
idxNew.Name = idx.Name
idxNew.Primary = idx.Primary
idxNew.Required = idx.Required
idxNew.Unique = idx.Unique
tdNew.Indexes.Append idxNew
End If
Next
dbNew.TableDefs.Append tdNew
End If
Next
' Copy the relations
Dim relNew As Relation
For Each rel In db.Relations
Set relNew = dbNew.CreateRelation(rel.Name, rel.Table, rel.ForeignTable, rel.Attributes)
Set fldNew = relNew.CreateField(rel.Fields(0).Name)
fldNew.ForeignName = rel.Fields(0).ForeignName
relNew.Fields.Append fldNew
dbNew.Relations.Append relNew
Next
' Copy QueryDefs
Dim qd As QueryDef, qdNew As QueryDef
For Each qd In db.QueryDefs
Set qdNew = dbNew.CreateQueryDef(qd.Name, qd.SQL)
' Idiotic: In contrast to Tables, Fields, Indices, Relations, ...
' no Append() necessary or even alowed for QueryDefs
Next
' Copy all records from old to new database
If Not CopyData(db, dbNew) Then
' If this was not successful, display messagebox, close and remove new database
gsMsg = "The necessary database update failed." & vbCrLf & vbCrLf & _
"Please contact software vendor!"
MsgBox gsMsg, vbInformation, gUI.FMain.Caption
dbNew.Close
Kill sNewDBPath
Exit Sub
End If
' Close old and new database
db.Close
dbNew.Close
' Get file path for a backup of old database
Dim sOldDBPath As String: sOldDBPath = Replace(sDbPath, ".mdb", "Old.mdb")
' Backup old database
CopyAnyFile sDbPath, sOldDBPath
Kill sDbPath
' Make new database to current database
CopyAnyFile sNewDBPath, sDbPath
Kill sNewDBPath
' Open the new database
Set db = OpenDatabase(sDbPath)
Exit Sub
ErrorUpdateDBReferenceHandling:
#If afDebug Then
Debug.Print GetErrorInfo(Err)
Stop
Resume
#End If
' Show error message
DisplayErrorMsg "Could not run action to end. Reason: ", _
Err.Number, Err.Description, msMODULENAME, sPROCEDURENAME, Erl
End Sub
Sie mögen folgende Schreibweise nicht?
Dim sNewDBPath As String: sNewDBPath = Replace(sDbPath, ".mdb", "New.mdb")
Ich eigentlich auch nicht. Lieber würde ich schreiben:
Dim sNewDBPath As String = Replace(sDbPath, ".mdb", "New.mdb")
Da das aber in VB 6.0 nicht geht, habe ich mir die obenstehende Alternative angewöhnt. Natürlich können Sie den Quellcode auf die gewöhnliche Schreibweise anpassen.
Dim sNewDBPath As String
sNewDBPath = Replace(sDbPath, ".mdb", "New.mdb")
Ist so etwas ähnliches wie eine Collection nur flexibler und Bestandteil der Microsoft Scripting Runtime (scrrun.dll). Näheres dazu unter INFO: VB 6.0 Readme Part 13: Dictionary Object.
'--------------------------------------------------------------------------------
' Procedure......: UpdateDBReferenceHandlingDone
' Author.........: Ralf Kunsmann
' Date...........: 2007 02 22
' Purpose........: Check, if Reference Handling update is already done.
' Hint...........: It's done if the field size is '50'
'--------------------------------------------------------------------------------
Private Function UpdateDBReferenceHandlingDone(db As Database) As Boolean
Dim td As TableDef: Set td = GetDatabaseTable(db, "ToolTypes")
Dim fld As Field: Set fld = GetDatabaseField(td, "RUID")
UpdateDBReferenceHandlingDone = fld.Size = 50
End Function
'--------------------------------------------------------------------------------
' Procedure......: GetDatabaseTable
' Author.........: Ralf Kunsmann
' Date...........: 2002 12 09
' Purpose........: Get reference to a certain table in a database.
' Result.........: On success: Object reference to table, else Nothing
' In parameters..: Object Reference: Database
' String: Name of table
'--------------------------------------------------------------------------------
Public Function GetDatabaseTable(Database As Database, TableName As String) As TableDef
#If Not afDebug Then
On Error Resume Next
#End If
Dim td As TableDef
For Each td In Database.TableDefs
If td.Name = TableName Then
Set GetDatabaseTable = td
Exit Function
End If
Next
Set GetDatabaseTable = Nothing
End Function
'--------------------------------------------------------------------------------
' Procedure......: GetDatabaseField
' Author.........: Ralf Kunsmann
' Date...........: 2002 12 09
' Purpose........: Get reference to a certain field in a database table.
' Result.........: On success: Object reference to field, else Nothing
' In parameters..: Object Reference: Database table
' String: Name of field
'--------------------------------------------------------------------------------
Public Function GetDatabaseField(TD As TableDef, FLD As String) As Field
#If Not afDebug Then
On Error Resume Next
#End If
Dim fld As Field
For Each fld In TD.Fields
If fld.Name = FLD Then
Set GetDatabaseField = fld
Exit Function
End If
Next
Set GetDatabaseField = Nothing
End Function
'--------------------------------------------------------------------------------
' Procedure......: CopyData
' Author.........: Ralf Kunsmann
' Date...........: 2007 02 26
' Purpose........: Copy all records of all tables from old to new database.
' Hint...........: Ordner is not by chance. Relations must be considered.
'--------------------------------------------------------------------------------
Private Function CopyData(db As Database, dbNew As Database) As Boolean
Const sPROCEDURENAME As String = "CopyData"
On Error GoTo ErrorCopyData
If Not CopyTableData(db, dbNew, "Devices") Then Exit Function
If Not CopyTableData(db, dbNew, "DevicesDefault") Then Exit Function
If Not CopyTableData(db, dbNew, "DevicesMflSh") Then Exit Function
If Not CopyTableData(db, dbNew, "DevicesMflShChannels") Then Exit Function
If Not CopyTableData(db, dbNew, "ToolTypes") Then Exit Function
If Not CopyTableData(db, dbNew, "ConfigUses") Then Exit Function
If Not CopyTableData(db, dbNew, "Configs") Then Exit Function
If Not CopyTableData(db, dbNew, "ConfigsDefault") Then Exit Function
If Not CopyTableData(db, dbNew, "ConfigsOptionPack") Then Exit Function
If Not CopyTableData(db, dbNew, "ConfigSecs") Then Exit Function
If Not CopyTableData(db, dbNew, "ConfigSecsDefault") Then Exit Function
If Not CopyTableData(db, dbNew, "ConfigSecsDefaultPc") Then Exit Function
If Not CopyTableData(db, dbNew, "ConfigSecsDefaultSc") Then Exit Function
If Not CopyTableData(db, dbNew, "ConfigDetails") Then Exit Function
If Not CopyTableData(db, dbNew, "ConfigDetailsDefault") Then Exit Function
If Not CopyTableData(db, dbNew, "ConfigDetailsDefaultFlightRec") Then Exit Function
If Not CopyTableData(db, dbNew, "ConfigDetailsMflSenSeq") Then Exit Function
If Not CopyTableData(db, dbNew, "ConfigDetailsScMfl") Then Exit Function
If Not CopyTableData(db, dbNew, "ConfigDetailsScMflSh") Then Exit Function
If Not CopyTableData(db, dbNew, "Projects") Then Exit Function
If Not CopyTableData(db, dbNew, "ItemValuesBoolean") Then Exit Function
If Not CopyTableData(db, dbNew, "ItemValuesDouble") Then Exit Function
If Not CopyTableData(db, dbNew, "ItemValuesLong") Then Exit Function
If Not CopyTableData(db, dbNew, "ItemValuesText") Then Exit Function
If Not CopyTableData(db, dbNew, "MN_Configs_Projects") Then Exit Function
If Not CopyTableData(db, dbNew, "Version") Then Exit Function
CopyData = True
Exit Function
ErrorCopyData:
#If afDebug Then
Debug.Print GetErrorInfo(Err)
Stop
Resume
#End If
' Remember: Don't risk to change Err object contents before it's read!!!
Dim sErr As String, iErr As Long
sErr = Err.Description: iErr = Err.Number
Err.Raise iErr, msMODULENAME & "." & sPROCEDURENAME, sErr
End Function
'--------------------------------------------------------------------------------
' Procedure......: CopyTableData
' Author.........: Ralf Kunsmann
' Date...........: 2007 02 26
' Purpose........: Copy records of a certain database table from one database
' to another.
'--------------------------------------------------------------------------------
Public Function CopyTableData(dbSource As Database, _
dbTarget As Database, _
TableName As String) As Boolean
Const sPROCEDURENAME As String = "CopyTableData"
On Error GoTo ErrorCopyTableData
gUI.DisplayStateMsg "Copying table " & TableName & " ..."
Dim rsSource As Recordset
Set rsSource = dbSource.OpenRecordset(TableName, dbOpenSnapshot)
Dim rsTarget As Recordset
Set rsTarget = dbTarget.OpenRecordset(TableName, dbOpenDynaset)
Do While Not rsSource.EOF
rsTarget.AddNew
If Not CopyRecordsetData(rsSource, rsTarget, False) Then Exit Function
rsTarget.Update
rsSource.MoveNext
Loop
CopyTableData = True
Exit Function
ErrorCopyTableData:
#If afDebug Then
Debug.Print GetErrorInfo(Err)
Stop
Resume
#End If
' Remember: Don't risk to change Err object contents before it's read!!!
Dim sErr As String, iErr As Long
sErr = Err.Description: iErr = Err.Number
Err.Raise iErr, msMODULENAME & "." & sPROCEDURENAME, sErr
End Function
'--------------------------------------------------------------------------------
' Procedure.....: CopyRecordsetData
' Author........: Ralf Kunsmann
' Date..........: 2001 06 22
' Purpose.......: Copy VisiPIG project data from one recordset to another (Part
' of work, when complete projects are copied).
' Result........: True on success, else False
' In parameters.: Recordset with the source data;
' Recordset where data has to be copied to;
' Boolean saying, if the target recordset has to be set to edit
' mode before copying (and has to be updated at the end of work).
'--------------------------------------------------------------------------------
Public Function CopyRecordsetData(rsSource As Recordset, _
rsTarget As Recordset, _
EditAndUpdate As Boolean) As Boolean
Const sPROCEDURENAME As String = "CopyRecordsetData"
Dim fld As Field
On Error GoTo ErrorCopyRecordsetData
If EditAndUpdate Then rsTarget.Edit
For Each fld In rsTarget.Fields
If Not (fld.Attributes And dbAutoIncrField) = dbAutoIncrField Then _
rsTarget(fld.Name) = rsSource(fld.Name)
Next
If EditAndUpdate Then rsTarget.Update
CopyRecordsetData = True
Exit Function
ErrorCopyRecordsetData:
' It can easily be the case that a field is target database in not present in
' source database. This can be ignored becouse the field contents will be set
' to the default value.
If Err.Number = 3265 Then Resume Next ' Item not found in collection.
#If afDebug Then
Debug.Print GetErrorInfo(Err)
Stop
Resume
#End If
Dim sErr As String, iErr As Long
sErr = Err.Description: iErr = Err.Number
Err.Raise iErr, msMODULENAME & "." & sPROCEDURENAME, sErr
End Function
'--------------------------------------------------------------------------------
' Procedure......: GetErrorInfo
' Author.........: Ralf Kunsmann
' Date...........: 2004 10 12
' Purpose........: Extract information about a code error from the Error object.
'--------------------------------------------------------------------------------
Public Function GetErrorInfo(Err As ErrObject) As String
Const iCOMERRMASK As Long = &H7000FFFF
Const iFACILITYMASK As Long = &HF0000
Const iFACILITY_AAF As Long = 18 ' 00000010010
Const iFACILITY_ACS As Long = 20 ' 00000010100
Const iFACILITY_BACKGROUNDCOPY As Long = 32 ' 00000100000
Const iFACILITY_CERT As Long = 11 ' 00000001011
Const iFACILITY_COMPLUS As Long = 17 ' 00000010001
Const iFACILITY_CONFIGURATION As Long = 33 ' 00000100001
Const iFACILITY_CONTROL As Long = 10 ' 00000001010
Const iFACILITY_DISPATCH As Long = 2 ' 00000000010
Const iFACILITY_DPLAY As Long = 21 ' 00000010101
Const iFACILITY_HTTP As Long = 25 ' 00000011001
Const iFACILITY_INTERNET As Long = 12 ' 00000001100
Const iFACILITY_ITF As Long = 4 ' 00000000100
Const iFACILITY_MEDIASERVER As Long = 13 ' 00000001101
Const iFACILITY_MSMQ As Long = 14 ' 00000001110
Const iFACILITY_NULL As Long = 0 ' 00000000000
Const iFACILITY_RPC As Long = 1 ' 00000000001
Const iFACILITY_SCARD As Long = 16 ' 00000010000
Const iFACILITY_SECURITY As Long = 9 ' 00000001001
Const iFACILITY_SETUPAPI As Long = 15 ' 00000001111
Const iFACILITY_SSPI As Long = 9 ' 00000001001
Const iFACILITY_STORAGE As Long = 3 ' 00000000011
Const iFACILITY_SXS As Long = 23 ' 00000010111
Const iFACILITY_UMI As Long = 22 ' 00000010110
Const iFACILITY_URT As Long = 19 ' 00000010011
Const iFACILITY_WIN32 As Long = 7 ' 00000000111
Const iFACILITY_WINDOWS As Long = 8 ' 00000001000
Const iFACILITY_WINDOWS_CE As Long = 24 ' 00000011000
Dim iNumber As Long
Dim iFacility As Long
Dim sSource As String
Dim sDescription As String
Dim sErr As String
' Get info from Error object
iNumber = Err.Number
sSource = Err.Source
sDescription = Err.Description
' Activate error handle only now (because it will reset the Err object)
On Error Resume Next
' Extract facility and error number (COM errors contain 1 in MS bit)
iFacility = (iNumber And iFACILITYMASK) / &H10000
iNumber = iNumber And iCOMERRMASK
' Get source
sErr = " Source: " & sSource & vbCrLf & " Facility: "
' Get facility
Select Case iFacility
Case iFACILITY_AAF: sErr = sErr & "AAF" & vbCrLf
Case iFACILITY_ACS: sErr = sErr & "ACS" & vbCrLf
Case iFACILITY_BACKGROUNDCOPY: sErr = sErr & "BACKGROUNDCOPY" & vbCrLf
Case iFACILITY_CERT: sErr = sErr & "CERT" & vbCrLf
Case iFACILITY_COMPLUS: sErr = sErr & "COMPLUS" & vbCrLf
Case iFACILITY_CONFIGURATION: sErr = sErr & "CONFIGURATION" & vbCrLf
Case iFACILITY_CONTROL: sErr = sErr & "Control" & vbCrLf
Case iFACILITY_DISPATCH: sErr = sErr & "DISPATCH" & vbCrLf
Case iFACILITY_DPLAY: sErr = sErr & "DPLAY" & vbCrLf
Case iFACILITY_HTTP: sErr = sErr & "HTTP" & vbCrLf
Case iFACILITY_INTERNET: sErr = sErr & "INTERNET" & vbCrLf
Case iFACILITY_ITF: sErr = sErr & "ITF" & vbCrLf
Case iFACILITY_MEDIASERVER: sErr = sErr & "MEDIASERVER" & vbCrLf
Case iFACILITY_MSMQ: sErr = sErr & "MSMQ" & vbCrLf
Case iFACILITY_NULL: sErr = sErr & "Undefined" & vbCrLf
Case iFACILITY_RPC: sErr = sErr & "RPC" & vbCrLf
Case iFACILITY_SCARD: sErr = sErr & "SCARD" & vbCrLf
Case iFACILITY_SECURITY: sErr = sErr & "SECURITY or SSPI" & vbCrLf
Case iFACILITY_SETUPAPI: sErr = sErr & "SETUPAPI" & vbCrLf
Case iFACILITY_STORAGE: sErr = sErr & "STORAGE" & vbCrLf
Case iFACILITY_SXS: sErr = sErr & "SXS" & vbCrLf
Case iFACILITY_UMI: sErr = sErr & "UMI" & vbCrLf
Case iFACILITY_URT: sErr = sErr & "URT" & vbCrLf
Case iFACILITY_WIN32: sErr = sErr & "Win32" & vbCrLf
Case iFACILITY_WINDOWS: sErr = sErr & "WINDOWS" & vbCrLf
Case iFACILITY_WINDOWS_CE: sErr = sErr & "WINDOWS_CE" & vbCrLf
Case Else: sErr = sErr & "Unknown" & vbCrLf
End Select
' Get error number and description and return
GetErrorInfo = sErr & " Number: " & CStr(iNumber) & vbCrLf & _
" Description: " & sDescription
End Function
Wenn Sie wissen wollen, was so alles auf Ihrem PC passiert!
Tischrechner als Software.
Jetzt herunterladen und kostenlos testen!
Tastenkombinationen können PC-Arbeit erheblich beschleunigen.
Eine Reihe von kostenlosen Online-Berechnungen zur Erleichterung der täglichen Arbeit.
Sicherheit im PC-Bereich
Es existiert eine kostenlose, einfache und äußert effektive Methode, fast alle Viren, Trojaner, Würmer ...