I have an Access database that I need to reverse engineer into a website (app & data).
I just tried opening it with Outlook 2013 but I get an error that the database has replication enabled and it created with an earlier version of Access (something to that affect).
I assume it's just the replication that's the issue. Is there a way to remove the replication so I can poen it in Access 2013?
So it's not an easy job to unreplicate a database.
First I found WV Mitchell's VBA script that copies your tables into a new database:
http://www.wvmitchell.com/tips/Removing%20Access%20Replication.htm
That doesn't copy the Primary Keys or the Relationships, so I wrote a few quick and dirty VBA scripts to help with that.
Step 1: In the MakeOneTable function from the above script, I added this (above the db.Close line):
Dim td As TableDef
Dim idxLoop As Index
Set td = db.TableDefs(TableName)
For Each idxLoop In td.Indexes
If idxLoop.Primary = True Then
Dim colnames As String
colnames = idxLoop.Fields
colnames = Replace(colnames, ";+", "],[")
colnames = Replace(colnames, "+", "[")
colnames = colnames & "]"
Debug.Print "DoCmd.RunSQL ""CREATE INDEX [PrimaryKey] ON [" & TableName & "] (" & colnames & ") With Primary;"""
Exit For
End If
Next idxLoop
That will output to the Debug (Immediate) Window some VBA code for creating the primary keys (as well as creating the new database and exporting the data to it - MV Mitchell's code). Copy that VBA code, put it into a module/macro in the new database, and run it - and it should create the Primary Keys.
Step 2: In the Old (replicated) database, run this VBA code, which will generate some more VBA code for creating the relationships:
Sub GenerateRelationshipCode()
Dim db As DAO.Database
Set db = CurrentDb()
Dim rs As DAO.Recordset
Set rs = db.OpenRecordset("SELECT DISTINCT szRelationship,szObject,szReferencedObject FROM MSysRelationships ORDER BY szObject,szReferencedObject")
rs.MoveFirst
Do While Not rs.EOF
Dim rsFields As DAO.Recordset
Set rsFields = db.OpenRecordset("SELECT * FROM MSysRelationships WHERE szRelationship = '" & Replace(rs.Fields(0), "'", "''") & "'")
Dim PKFields As String, PKTable As String, FKFields As String, FKTable As String
PKFields = "": PKTable = "": FKFields = "": FKTable = ""
Do While Not rsFields.EOF
PKFields = PKFields & rsFields("szReferencedColumn") & ","
PKTable = rsFields("szReferencedObject")
FKFields = FKFields & rsFields("szColumn") & ","
FKTable = rsFields("szObject")
rsFields.MoveNext
Loop
PKFields = Left(PKFields, Len(PKFields) - 1)
FKFields = Left(FKFields, Len(FKFields) - 1)
Debug.Print "Call AddRelationship(""" & rs.Fields(0) & """, """ & FKTable & """, """ & FKFields & """, """ & PKTable & """, """ & PKFields & """)"
rs.MoveNext
Loop
Set db = Nothing
End Sub
You will need to run the output from the above against the new database, but you will also need this function:
Public Sub AddRelationship(Name As String, FKTable As String, FKFields As String, PKTable As String, PKFields As String)
Dim strSQL As String
Dim db As DAO.Database
Set db = CurrentDb()
Name = "FK_" & Replace(FKTable, " ", "") & "_" & Replace(PKTable, " ", "") 'only enable this line if there aren't multiple relationships between same 2 tables
strSQL = "ALTER TABLE [" & FKTable & "] " & _
" add constraint " & Name & " foreign key (" & FKFields & ") " & _
" references [" & PKTable & "](" & PKFields & ") "
db.Execute strSQL, dbFailOnError
Set db = Nothing
End Sub
Run the generated VBA code in a module with the above function, and it should rebuild your relationships in the new database.
Note this code is quick & dirty so might need some bug fixes for your database/data.
Related
I would like to delete all records in the field "pathway" in the table CUSTOMER that is in the backend (offline) database.
So far I have this, but it does not work with DELETE statement
Sub delpath()
Dim dbinputC As String
dbinputC = "[" & Application.CurrentProject.Path & "\CUSTOMER.accdb" & "]"
DoCmd.RunSQL "DELETE pathway FROM " & dbinputC & ".SPECPATH (WHERE pathway <> Null);"
End Sub
Or
Dim dbinputC As String
dbinputC = "'" & Application.CurrentProject.Path & "\CUSTOMER.accdb" & "'"
DoCmd.RunSQL "DELETE pathway FROM SPECPATH (WHERE pathway <> Null) IN " & dbinputC & ";"
Private Sub Test_Clear_Data
Clear_Data "SPECPATH", "Pathway"
End Sub
Private Sub Clear_Data(Table_Name as String, Column_Name As String)
Dim Connection_Path As String
Dim Source_Recset As Object
'Assumes "Clear_Data_Query" already exists
Set Source_Recset = CurrentDB.QueryDefs("Clear_Data_Query")
Source_Recset.SQL = CStr("Update " & Table_Name & " SET [" & Table_Name & "].[" & Column_Name & "] = NULL WHERE [" & Table_Name & "].[" & Column_Name & "] IS NOT Null" & ";")
Source_Recset.Execute
Source_Recset.Close
End Sub
Source_Recset.SQL should get "UPDATE SPECPATH SET [SPECPATH].[Pathway] = NULL Where [SPECPATH].[Pathway] IS NOT NULL;" If table is SPECPATH and Column Name is Pathway
Since you are referencing an offline database i included code to append the table temporarily and remove it after (code is not needed if you leave the table defined in the access file"
Private Sub Clear_Offline_Data(Share_Folder as String, File_Name as String, Table_Name as String, Column_Name As String)
Dim Connection_Path As String
Dim Source_Recset As Object
Dim Destination_Recset As Object
'Create Table To Network Data
Set Destination_Recset = currentDB.CreateTableDef("Offline_Data_Table")
Connection_Path = ";DATABASE=" & ShareFolder & "\" & File_Name
Destination_Recset.Connect = Connection_Path
Destination_Recset.SourceTableName = Table_Name
currentDB.TableDefs.Append Destination_Recset
currentDB.TableDefs.Refresh
'Create Temp_Data From Network Table
Set Source_Recset = CurrentDB.CreateQueryDef("Clear_Data_Query")
Source_Recset.SQL = CStr("Update Offline_Data_Table SET [Offline_Data_Table]." & Column_Name & " = NULL WHERE [Offline_Data_Table]." & Column_Name & " IS NOT Null" & ";")
Source_Recset.Execute
Source_Recset.Close
'Remove Table to network data
currentDB.TableDefs.Delete "Offline_Data_Table"
currentDB.TableDefs.Refresh
'Remove Query
currentDB.QueryDefs.Delete "Clear_Data_Query"
currentDB.QueryDefs.Refresh
End Sub
A delete query deletes rows, not fields.
You must use an update query that updates field pathway.
I guess that it worked after all:
Sub delpath()
Dim dbinputC As String
dbinputC = "[" & Application.CurrentProject.Path & "\CUSTOMER.accdb" & "]"
DoCmd.RunSQL "DELETE pathway FROM " & dbinputC & ".SPECPATH WHERE pathway Is Not Null;"
End Sub
I have a table, which was pulled out of some XML data. I'm trying to do a cross reference, so I can line out a plan for organizing the data. This 1 table has a list of variables. Fields of different data types, computations, as well as dialogs. One of the columns has options. If the data type of the variable is a dialog, its options has a list of variables, separated by a semi-colon.
So the main table has a structure like so:
For the dialog records I need to look through their options column and insert records into a normalized table. For each field, in that column, I want to add a record with that dialog name, and the ID of the row in that table (I added a PK to the table). For instance, in the dialog record, Options column, there is a field in there called BusinessName TE. I need to search this main table for the PK ID of the row that has a variable name of the same. I need to put that record's ID with the name of the dialog, and insert both into a new table I set up. This will create a cross reference for me, so I can know which variables are being used by which dialogs.
I appreciate any help anyone can give. I see stuff about using a split function, arrays and looping through to get each value, but the examples I'm finding are for strings, not a column in a table.
Thanks!
Edit: Adding in the VBA code I'm working with. I attached it to a button on a form, just so I could click to run it.
Private Sub RunParse_Click()
Dim db As DAO.Database
Dim rs As Recordset
Set db = CurrentDb()
Dim sqlStr, insertSQL, arrayVal As String
Dim TestArray As Variant
Dim Options As String
Dim Dialog As String
Dim FieldName As Long
Dim i As Integer
sqlStr = "SELECT [MASTER Fields].Options,[MASTER Fields].[Variable Name] FROM [MASTER Fields] WHERE ((([MASTER Fields].[Variable Type])='dialog'));"
Set rs = db.OpenRecordset(sqlStr)
rs.MoveLast
rs.MoveFirst
Do While Not rs.EOF
Options = rs.Fields(0)
Dialog = rs.Fields(1)
If InStr(Options, ";") Then
TestArray = Split(Options, ";")
For i = 0 To UBound(TestArray) - LBound(TestArray) + 1
If TestArray(i) <> "" Then
arrayVal = TestArray(i)
FieldName = DLookup("ID", "MASTER Fields", "[Variable Name] = " & "'" & arrayVal & "'")
insertSQL = "INSERT INTO FieldTemplatesUse(FID, TemplateAK) " _
& "VALUES(""" & FieldName & """, """ & Dialog & """)"
DoCmd.RunSQL (insertSQL)
End If
Next i
End If
rs.MoveNext
Loop
End Sub
right now on the line that says
If TestArray(i) <> "" Then
creates an error ""
If anyone can help, I'd really appreciate it!
Another Edit:
Parfait figured out my issue. I'm posting the final code I am using, in case it helps someone else! p.s. I added a condition to check if the dlookup is successful, and trap failures in a failures table. That way I can check those out afterward.
Private Sub RunParse_Click()
Dim db As DAO.Database
Dim rs As Recordset
Set db = CurrentDb()
Dim sqlStr, insertSQL, arrayVal As String
Dim TestArray As Variant
Dim Options As String
Dim Dialog As String
Dim FieldName As Long
Dim i As Integer
sqlStr = "SELECT [Master Fields].Options,[Master Fields].[Variable Name] FROM [Master Fields] WHERE ((([Master Fields].[Variable Type])='dialog'));"
Set rs = db.OpenRecordset(sqlStr)
rs.MoveLast
rs.MoveFirst
Do While Not rs.EOF
Options = rs.Fields(0)
Dialog = rs.Fields(1)
If InStr(Options, ";") Then
TestArray = Split(Options, ";")
For i = 0 To UBound(TestArray) - LBound(TestArray)
If TestArray(i) <> "" Then
arrayVal = TestArray(i)
If Not (IsNull(DLookup("ID", "Master Fields", "[Variable Name] = " & "'" & arrayVal & "'"))) Then
FieldName = DLookup("ID", "Master Fields", "[Variable Name] = " & "'" & arrayVal & "'")
insertSQL = "INSERT INTO FieldTemplatesUse(FID, TemplateAK) " _
& "VALUES(""" & FieldName & """, """ & Dialog & """)"
DoCmd.RunSQL (insertSQL)
'MsgBox "Adding ID = " & FieldName & "for Dialog: " & Dialog & "Now"
Else
insertSQL = "INSERT INTO tblFieldsNotFound(Dialog, FieldNotFound) " _
& "VALUES(""" & Dialog & """, """ & arrayVal & """)"
DoCmd.RunSQL (insertSQL)
End If
End If
Next i
End If
rs.MoveNext
Loop
MsgBox "All Done!"
End Sub
I need to scrub a regularly received Access database so that all of its tables can be exported to "clean" CSVs and then imported by Base SAS via PROC IMPORT.
I am not experienced with Access VBA or programming in general, but I attempted to kitbash a script to loop through every field in every table and replace certain characters. It doesn't appear to work and I get several "Type Conversion Failure" errors while it's running.
Public Sub ReplaceCharAllTables()
Dim strSQL As String
Dim fld As DAO.Field
Dim db As DAO.Database
Set db = CurrentDb()
Dim obj As AccessObject, dbs As Object
Set dbs = Application.CurrentData
' Cycle through all tables in database
For Each obj In dbs.AllTables
' Cycle through all fields in the table
For Each fld In db.TableDefs("[" & obj.Name & "]").Fields
If fld.Type = dbText And Not IsNull(fld) Then
strSQL = "Update [" & obj.Name & "] Set [" & fld.Name & "]= Replace([" & fld.Name & "],Chr(10),'. ')"
DoCmd.RunSQL strSQL
strSQL = "Update [" & obj.Name & "] Set [" & fld.Name & "]= Replace([" & fld.Name & "],Chr(13),'. ')"
DoCmd.RunSQL strSQL
End If
Next
Next obj
End Sub
Note that this particular code current only attempts to remove two characters. It's just a temporary testbed.
EDIT 2016.11.30: Just wanted to say that Andre's solution was perfect. I ended up needing to make a couple minor tweaks, particularly to also look at "memo" fields in addition to text fields and to write the helpful debug info to a text file rather than to the size-limited Immediate Window. Looping through an array of character codes was deceptively clever.
Public Sub ReplaceCharAllTables()
Dim strSQL As String
Dim fld As DAO.Field
Dim db As DAO.Database
Dim td As DAO.TableDef
Dim strFld As String
Dim arCharCodes As Variant
Dim code As Variant
Dim strFolder As String
Dim n As Integer
Dim strUpdate As String
' Get stuff setup save debug.print log file
strFolder = Application.CurrentProject.Path & "\" & Application.CurrentProject.Name & "_RemoveCharLog.txt"
n = FreeFile()
Open strFolder For Output As #n
' all charcodes to replace
arCharCodes = Array(10, 13, 44)
Set db = CurrentDb()
' Cycle through all tables in database
For Each td In db.TableDefs
' Ignore system tables
If Not (td.Name Like "MSys*" Or td.Name Like "USys*") Then
' Cycle through all fields in the table
For Each fld In td.Fields
If fld.Type = dbText Or fld.Type = dbMemo Then ' Check if field is text or memo
' Cycle through all character codes to remove
For Each code In arCharCodes
strFld = "[" & fld.Name & "]"
strSQL = "UPDATE [" & td.Name & "] " & _
"SET " & strFld & " = Replace(" & strFld & ", Chr(" & code & "), '. ') " & _
"WHERE " & strFld & " LIKE '*" & Chr(code) & "*'"
db.Execute strSQL
strUpdate = "Updated " & db.RecordsAffected & " records."
'Start printing logs
Debug.Print strSQL
Debug.Print strUpdate
Print #n, strSQL
Print #n, strUpdate
Next code
End If
Next fld
End If
Next td
End Sub
In principal there is nothing wrong with your code as far as I can see. The main problem may be that it also attempts to update all system tables - check "System objects" in the Navigation options of the navigation pane to see them.
They start with MSys or USys.
A few other things to improve:
You need the TableDef objects anyway, so you can directly loop them instead of AllTables
A table field cannot be Null, so this check isn't needed
For efficiency you want to only update rows where the column actually contains the searched character, so I add a WHERE clause
To avoid duplicate code, put all character codes to replace in an array for an additional loop.
Use db.Execute instead of DoCmd.RunSQL: it avoids the need for DoCmd.SetWarnings False, and gives you the number of affected records.
My suggestion:
Public Sub ReplaceCharAllTables()
Dim strSQL As String
Dim fld As DAO.Field
Dim db As DAO.Database
Dim td As DAO.TableDef
Dim strFld As String
Dim arCharCodes As Variant
Dim code As Variant
' all charcodes to replace
arCharCodes = Array(10, 13)
Set db = CurrentDb()
' Cycle through all tables in database
For Each td In db.TableDefs
' Ignore system tables
If Not (td.Name Like "MSys*" Or td.Name Like "USys*") Then
' Cycle through all fields in the table
For Each fld In td.Fields
If fld.Type = dbText Then
For Each code In arCharCodes
strFld = "[" & fld.Name & "]"
strSQL = "UPDATE [" & td.Name & "] " & _
"SET " & strFld & " = Replace(" & strFld & ", Chr(" & code & "), '. ') " & _
"WHERE " & strFld & " LIKE '*" & Chr(code) & "*'"
Debug.Print strSQL
db.Execute strSQL
Debug.Print "Updated " & db.RecordsAffected & " records."
Next code
End If
Next fld
End If
Next td
End Sub
If this still gives errors, check the specific SQL (Ctrl+g shows the output of Debug.Print) - what column data type does it want to update?
I created an Access database which I want to distribute to a small group. While I can always export the tables in excel and merge them/append data there, is there a way to sync the databases, maybe by using VBA?
To expound further, in one form in the database application, a sync button may exist, and onclick, a dialog box may open to look for the accdb to sync with. What ensues is that the VBA will "sync" the table (which of course is of the same structure) in question between the two accdbs.
Is this possible? Insights will be good. Thank you!
Yes, it is perfectly possible. Here are some notes on comparing two DBs and logging changes.
The procedure requires the following at the top of the module:
Dim strFileNew As String
Dim strFileOld As String
Dim strLog As String
Dim dbOld As Database
The variables might contain:
strLog = "log.txt"
strFileNew = "z:\docs\dbNew.mdb"
strFileOld = "z:\docs\dbOld.mdb"
Set dbOld = OpenDatabase(strFileOld)
Then the comparison:
Sub LogCompareDB(db As Database)
''References : Windows Script Host Object Model
'' This is set by default for a number of versions
'' : Microsoft DAO x.x Object Library
'' For 2010, the DAO library is called
'' :Microsoft Office 12.0 Access Database Engine Object Library
Dim tdf As TableDef
Dim rs0 As DAO.Recordset
Dim rs1 As DAO.Recordset
Dim fld As DAO.Field
Dim idx As Index
Dim idxPrimary As Index
Dim strIndexList As String
Dim strIndex As String
Dim strID As String
Dim strSQL As String
Dim strChanged As String
Dim blnNew As Boolean
Dim fs As New FileSystemObject
Dim ts As TextStream
Set ts = fs.CreateTextFile(strLog, True)
''For each table in the old database
''(It would probably be a good idea to check the
''new database for added tables)
For Each tdf In db.TableDefs
'' Skip system tables
If Left(tdf.Name, 4) <> "MSys" Then
strIndex = vbNullString
Set idxPrimary = Nothing
strIndexList = vbNullString
''Get the primary index and index fields
For Each idx In tdf.Indexes
If idx.Primary = True Then
Set idxPrimary = idx
For Each fld In idx.Fields
strIndex = strIndex & " AND t0.[" & fld.Name _
& "] = t1.[" & fld.Name & "]"
strIndexList = strIndexList & "," & fld.Name
Next
strIndex = Mid(strIndex, 5)
End If
Next
''There is no basis for comparison if there is no index.
''A unique index would also be a possibility, but hey, let's
''not go over the top :)
If strIndex > vbNullString Then
''Select all records from the table for both databases
strSQL = "SELECT * FROM [;DATABASE=" & strFileNew & "].[" _
& tdf.Name & "] As t0 LEFT JOIN [" _
& tdf.Name & "] As t1 ON " & strIndex
Set rs0 = db.OpenRecordset(strSQL)
''A convenient list of fields from the old database
''It would probably be a good idea to check the
''new database for added fields.
strSQL = "SELECT * FROM [;DATABASE=" & strFileOld & "].[" _
& tdf.Name & "] As t0 WHERE 1=2"
Set rs1 = db.OpenRecordset(strSQL)
Do While Not rs0.EOF
strID = vbNullString
blnNew = False
''If the index fields are null, then it is a new record
For Each fld In idxPrimary.Fields
strID = strID & fld.Name & ": " & rs0("[t0." & fld.Name & "]") & vbCrLf
If IsNull(rs0("[t1." & fld.Name & "]")) Then
blnNew = True
End If
Next
If blnNew Then
''Write to log
ts.WriteLine "NEW RECORD " & strID & vbCrLf
Else
''Not a new record, so is it a changed record?
strChanged = vbNullString
For Each fld In rs1.Fields
''No need to check index fields, because they are equal
If InStr(strIndexList, fld.Name) = 0 Then
''Add null string for purposes of comparison ''trailing
If "" & rs0("[t0." & fld.Name & "]") <> "" & rs0("[t1." & fld.Name & "]") Then
strChanged = strChanged & vbCrLf _
& fld.Name & " Is: " & Trim(rs0("[t0." & fld.Name & "]")) _
& " Was: " & Trim(rs0("[t1." & fld.Name & "]"))
End If
End If
Next
If strChanged <> vbNullString Then
''Write to log
ts.WriteLine "CHANGED RECORD " & strID
ts.WriteLine strChanged & vbCrLf
End If
End If
rs0.MoveNext
Loop
Else
ts.WriteLine "NO PRIMARY INDEX " & tdf.Name & vbCrLf
End If
End If
Next
ts.Close
FollowHyperlink strLog
End Sub
Option Compare Database
Private Sub Command4_Click()
Dim tablename1, tablename2 As String
tablename1 = Text0.Value
tablename2 = Text2.Value
'On Error GoTo Err_cmdValidateGeneralInfo_Click
Dim F As DAO.Field
Dim rs As DAO.Recordset
Dim rs1 As DAO.Recordset
Set curDB = CurrentDb()
'If Me.DateModified = Date Then
'Adds new employees to the TT_GeneralInfo table in the FTEI_PhoneBook.mdb - which is used thru out the AP databases.
' DoCmd.OpenQuery "qryEmpData_TT_General"
strsql = "Select * from " & tablename1
Set rs = curDB.OpenRecordset(strsql)
strsql1 = "Select * from " & tablename2
DoCmd.CopyObject , "Unmatched_records", acTable, tablename1
curDB.Execute "DELETE FROM Unmatched_records"
Set rs1 = curDB.OpenRecordset(strsql1)
Do Until rs.EOF
For Each F In rs.Fields
If rs.Fields(F.Name) <> rs1.Fields(F.Name) Then
'rs.Edit
strsql = "Select * into test from " & tablename1 & " where " & F.Name & " = """ & rs.Fields(F.Name) & """"
DoCmd.RunSQL strsql
If DCount(F.Name, "test") <> 0 Then
GoTo append_unmatch
'appending unmacthed records
append_unmatch:
strsql2 = "insert into Unmatched_records Select * from test"
DoCmd.RunSQL strsql2
'if record doesnt match move to next one
GoTo Nextrecord
End If
' rs.Fields(F.Name) = rs1.Fields(F.Name)
' rs.Update
End If
Next F
Nextrecord:
rs.MoveNext
rs1.MoveNext
Loop
If DCount("test", F.Name) <> 0 Then
MsgBox ("The two tables didnt match. Check table test for unmatching reocrds.")
Else
MsgBox ("Tables match!")
End If
End Sub
we have an issue with an access database we are upgrading to use SQL Server as its data store.
This particular database links to 2 sql databases, so I thought to simplify things, we have a view in the main database that linked to each table in the secondary database. That way access would only need to talk directly with one SQL database.
When we linked access to the database views we choose which fields were the primary keys so the views were not readonly. We have standard code that refreshes all links when a database opens to pickup any changes and the linked views become readonly because the primary key information is lost.
Is there a way of refreshing the links to views while retaining the primary key information?
John
I have included my entire ODBC Reconnect function below. This function is predicated with the idea that I have a table called rtblODBC which stores all of the information I need to do the reconnecting. If you implement this function, you will NOT need to worry about connecting to multiple SQL databases, as that is handled smoothly with each table to be reconnected having its own connection string.
When you get towards the end you will see that I use DAO to recreate the primary keys with db.Execute "CREATE INDEX " & sPrimaryKeyName & " ON " & sLocalTableName & "(" & sPrimaryKeyField & ")WITH PRIMARY;"
If you have any questions, please ask.
Public Function fnReconnectODBC( _
Optional bForceReconnect As Boolean _
) As Boolean
' Comments :
' Parameters: bForceReconnect -
' Returns : Boolean -
' Modified :
' --------------------------------------------------'
On Error GoTo Err_fnReconnectODBC
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim tdf As DAO.TableDef
Dim sPrimaryKeyName As String
Dim sPrimaryKeyField As String
Dim sLocalTableName As String
Dim strConnect As String
Dim varRet As Variant
Dim con As ADODB.Connection
Dim rst As ADODB.Recordset
Dim sSQL As String
If IsMissing(bForceReconnect) Then
bForceReconnect = False
End If
sSQL = "SELECT rtblODBC.LocalTableName, MSysObjects.Name, MSysObjects.ForeignName, rtblODBC.SourceTableName, MSysObjects.Connect, rtblODBC.ConnectString " _
& "FROM MSysObjects RIGHT JOIN rtblODBC ON MSysObjects.Name = rtblODBC.LocalTableName " _
& "WHERE (((rtblODBC.ConnectString)<>'ODBC;' & [Connect]));"
Set con = Access.CurrentProject.Connection
Set rst = New ADODB.Recordset
rst.Open sSQL, con, adOpenDynamic, adLockOptimistic
'Test the recordset to see if any tables in rtblODBC (needed tables) are missing from the MSysObjects (actual tables)
If rst.BOF And rst.EOF And bForceReconnect = False Then
'No missing tables identified
fnReconnectODBC = True
Else
'Table returned information, we don't have a perfect match, time to relink
Set db = CurrentDb
Set rs = db.OpenRecordset("rtblODBC", dbOpenSnapshot)
'For each table definition in the database collection of tables
For Each tdf In db.TableDefs
'Set strConnect variable to table connection string
strConnect = tdf.Connect
If Len(strConnect) > 0 And Left(tdf.Name, 1) <> "~" Then
If Left(strConnect, 4) = "ODBC" Then
'If there is a connection string, and it's not a temp table, and it IS an odbc table
'Delete the table
DoCmd.DeleteObject acTable, tdf.Name
End If
End If
Next
'Relink tables from rtblODBC
With rs
.MoveFirst
Do While Not .EOF
Set tdf = db.CreateTableDef(!localtablename, dbAttachSavePWD, !SourceTableName, !ConnectString)
varRet = SysCmd(acSysCmdSetStatus, "Relinking '" & !SourceTableName & "'")
db.TableDefs.Append tdf
db.TableDefs.Refresh
If Len(!PrimaryKeyName & "") > 0 And Len(!PrimaryKeyField & "") > 0 Then
sPrimaryKeyName = !PrimaryKeyName
sPrimaryKeyField = !PrimaryKeyField
sLocalTableName = !localtablename
db.Execute "CREATE INDEX " & sPrimaryKeyName & " ON " & sLocalTableName & "(" & sPrimaryKeyField & ")WITH PRIMARY;"
End If
db.TableDefs.Refresh
.MoveNext
Loop
End With
subTurnOffSubDataSheets
fnReconnectODBC = True
End If
rst.Close
Set rst = Nothing
con.Close
Set con = Nothing
Exit_fnReconnectODBC:
Set tdf = Nothing
Set rs = Nothing
Set db = Nothing
varRet = SysCmd(acSysCmdClearStatus)
Exit Function
Err_fnReconnectODBC:
fnReconnectODBC = False
sPrompt = "Press OK to continue."
vbMsg = MsgBox(sPrompt, vbOKOnly, "Error Reconnecting")
If vbMsg = vbOK Then
Resume Exit_fnReconnectODBC
End If
End Function
A good deal of DSN less code that re-links access tables to SQL server often deletes the links first, then recreates the link. The code then sets up the connection string. Thus, it is the deleting that causes you to lose what the primary key was/is.
I actually recommend that you modify your re-link code as to not delete the table links.
Try something like:
For Each tdfCurrent In dbCurrent.TableDefs
If Len(tdfCurrent.Connect) > 0 Then
If Left$(tdfCurrent.Connect, 5) = "ODBC;" Then
strCon = "ODBC;DRIVER={sql server};" & _
"SERVER=" & ServerName & ";" & _
"DATABASE=" & DatabaseName & ";" & _
"UID=" & UserID & ";" & _
"PWD=" & USERpw & ";" & _
"APP=Microsoft Office 2003;" & _
"WSID=" & WSID & ";"
End If
End If
tdfCurrent.Connect = strCon
tdfCurrent.RefreshLink
End If
Next tdfCurrent
This works a litte better for me (note the moved end if's):
Dim dbCurrent As Database
Set dbCurrent = CurrentDb()
StatusList.SetFocus
StatusList.AddItem ("starting... ")
I = DoEvents()
Dim tdfCurrent As DAO.TableDef
For Each tdfCurrent In dbCurrent.TableDefs
If Len(tdfCurrent.Connect) > 0 Then
If Left$(tdfCurrent.Connect, 5) = "ODBC;" Then
strCon = "ODBC;DRIVER={sql server};" & _
"SERVER=" & ServerName & ";" & _
"DATABASE=" & DatabaseName & ";" & _
"UID=" & UserID & ";" & _
"PWD=" & USERpw & ";" & _
"APP=Microsoft Office 2003;" & _
"WSID=" & WSID & ";"
StatusList.AddItem ("fixing " & tdfCurrent.Name)
tdfCurrent.Connect = strCon
tdfCurrent.RefreshLink
End If
End If
I = DoEvents()
Next tdfCurrent
StatusList.AddItem ("----Done.")
The ODBC check is correct, even though the "ODBC;" part doesn't show in the MSysObjects view.