Link multiple tables from one database - sql

I have actually this little function working as supposed:
Function createAttached(strTable As String, strPath As String, strBaseTable As String) As Boolean
On Error GoTo CreateAttachedError
Dim tdf As TableDef
Dim strConnect As String
Dim fRetval As Boolean
Dim myDB As Database
DoCmd.SetWarnings False
Set myDB = CurrentDb
Set tdf = myDB.CreateTableDef(strTable)
With tdf
.Connect = ";DATABASE=" & strPath
.SourceTableName = strBaseTable
End With
myDB.TableDefs.Append tdf
myDB.TableDefs.Refresh
fRetval = True
DoCmd.SetWarnings True
CreateAttachedExit:
createAttached = fRetval
Exit Function
CreateAttachedError:
If Err = 3110 Then
Resume CreateAttachedExit
Else
If Err = 3011 Then
Resume Next
Else
If Err = 3012 Then
Set tdf = myDB.TableDefs(strTable)
tdf.Connect = ";DATABASE=" & strPath
tdf.RefreshLink
fRetval = True
GoTo CreateAttachedExit
End If
End If
End If
End Function
This code works fine, I can call the function as many times as I want to add links to tables from another database. However, I have about 30 tables to import from the same database and this script restarts from scratch everytime I call it. Because the database is located on another server, it takes about 1 min to link the 30 tables.
Is there anything I could do with that function to make it work faster when I need to link multiple tables from the same database? I would like it to work with multiple strTable and strBaseTable in parameters instead of one (maybe arrays?), but I don't know how to do this.
Thank you.

You can loop through the tabledefs collection in the external database or use a table of tables that lists all the tables to be connected and the external database.
Dim db As Database
Dim ThisDb As Database
Set ThisDb = CurrentDB
sDb = "z:\docs\test.accdb"
Set db = OpenDatabase(sDb)
For Each tdf In db.TableDefs
''Connect
If Left(tdf.Name, 4) <> "MSys" Then
If IsNull(DlookUp("Name","MsysObjects","Type In (1,4,5,6) And Name='" _
& tdf.Name & "'")) Then
DoCmd.TransferDatabase acLink, "Microsoft Access", _
sDb, acTable, tdf.Name, tdf.Name
Else
If ThisDb.TableDefs(tdf.Name).connect <> vbNullString Then
ThisDb.TableDefs(tdf.Name).connect = ";DATABASE=" & sDb
ThisDb.TableDefs(tdf.Name).RefreshLink
End If
End If
End If
Next
A table of tables would work in a similar fashion in that you can select the database (select distinct) from the table and loop through that recordset attaching the tables in a further selection (select table where database ...)

What you could also do is set up the database connect in a different sub as well as the database disconnect (which I might be missing here?).
So then the code that calls this script normally (I assume you have a loop) will look like this:
call function/sub that opens the connection to DATABASE= strPath
your loop that calls Function createAttached
call function/sub that closes the connection to DATABASE= strPath
That way you avoid the repetitive (usually time consuming) connection to the external database

Related

How do I use VBA in MS Access to change the address of linked sharepoint lists

At work we have MS Access databases that use sharepoint lists as linked tables because we like the stability. The address of our sharepoint site is going to change. I want to run a VBA sub to change the tables from the old address to the new. Here is what I have so far, but there are two problems:
It does not automatically detect which tables are sharepoint lists without asking
Some tables are 'participating in one or more relationships' and give an error when deleted
How do I make it better?
Sub ChangeSPTables()
Dim db As DAO.Database
Dim tdf As DAO.TableDef
Dim MBr As VbMsgBoxResult
Dim N As String
Set db = CurrentDb
For Each tdf In db.TableDefs
If Not (tdf.Name Like "MSys*" Or tdf.Name Like "~*") Then
N = tdf.Name
MBr = MsgBox("Delete and relink " & N & "?", vbYesNoCancel)
If MBr = vbYes Then
DoCmd.DeleteObject acTable, N
DoCmd.TransferSharePointList acLinkSharePointList, "https://redacted", N
End If
End If
Next
Set tdf = Nothing
Set db = Nothing
End Sub
Related tables can't be deleted but can be dropped. Adding a table will also add related tables, so I was having troubles with duplicate adds. I stored all names to delete first and re-add later (if they don't already exist). The sharepoint address is part of the connection string so I was able to sift the tables I wanted like that.
Edit: Using the DoCmd.TransferSharePointList can cause lists with lookup columns to come in as read-only. For lists like that, delete and add them manually after running.
Sub ChangeSPTables()
'Step through all table definitions and check if the old sharepoint address appears in the connection string
'If so, stores the name of the table, deletes all such tables, and re-adds them from the new sharepoint address
'The list is used because sometimes other tables are brought in automatically as relationships and I don't
'want extra copies or instances where the table has a 1 on the end of the name
'Will not work on things that have been renamed since they were brought in from sharepoint
'You should probably only run this on a copy of the database for saftey's sake
Dim db As dao.Database
Dim tdf As dao.TableDef
Dim MBr As VbMsgBoxResult
Dim i As Long: i = 1
Dim ListNames(1 To 20) As String 'Assumed to be 20 or less
'The old and new sharepoint addresses
Dim OldSP As String
Dim NewSP As String
OldSP = "https://old"
NewSP = "https://new"
Set db = CurrentDb
For Each tdf In db.TableDefs
If Not (tdf.Name Like "MSys*" Or tdf.Name Like "~*") And InStr(1, tdf.Connect, OldSP) > 0 Then
ListNames(i) = tdf.Name
i = i + 1
Debug.Print tdf.Name 'For review of which tables were redone
db.Execute "drop table [" & tdf.Name & "]", dbFailOnError 'drop table avoids problems with relationships between tables
End If
Next
i = 1
Do Until ListNames(i) = ""
If IsNull(DLookup("Name", "MSysObjects", "Name='" & ListNames(i) & "'")) Then 'Some things get re-added by relationship
DoCmd.TransferSharePointList acLinkSharePointList, NewSP, ListNames(i)
End If
i = i + 1
Loop
'Hide the sharepoint tables
For Each tdf In db.TableDefs
If Not (tdf.Name Like "MSys*" Or tdf.Name Like "~*") And InStr(1, tdf.Connect, NewSP) > 0 Then
Application.SetHiddenAttribute acTable, tdf.Name, True
End If
Next
Set tdf = Nothing
Set db = Nothing
End Sub

how to rewrite code from DAO to ADO?

We got some old legacy application which was developed during 2000 and we have moved from access 2003 to 2007. When I am trying to run a module of an application, it is giving me an error:
"Run-time error 3847. ODBCDirect is no longer supported. Rewrite the code to use ADO instead of DAO".
And it highlights to the line Set WS = CreateWorkspace("NewWS", "", "", dbUseODBC). As I am really new to Access, I did research before posting this issue here but no luck. I am trying to rewrite the code to use ADO instead of DAO.
Following is my old vba code:
Public Function GetID (ByRef SegmentItem As clsSegmentDefinitions) As Long
Dim qdf As QueryDef
Dim qdfNewID As QueryDef
Dim rs As Recordset
Dim rsNewID As Recordset
Dim NaturalDescription As String
Dim WS As Workspace
Dim con As Connection
Set WS = CreateWorkspace("NewWS", "", "", dbUseODBC)
WS.DefaultCursorDriver = dbUseODBCCursor
Set con = WS.OpenConnection("", , , SQLConnectString)
DoCmd.Hourglass False
DoCmd.OpenForm " frmQuickAdd_AddNatural ", , , , , acDialog, SegmentItem.AddValue
DoCmd.Hourglass True
If Form_frmQuickAdd_AddNatural.Tag Then
Set qdf = con.CreateQueryDef("", "{ ? = call sp_Insert(?, ?, ?) }")
qdf.Parameters.Refresh
qdf![#prmDescription] = Left(Form_frmQuickAdd_AddNatural.txtSegmentDescription, 34)
qdf![#prmCreateUser] = CurrentUser
qdf![#prmProjectID] = 0
qdf.Execute
Set qdfNewID = CodeDb.CreateQueryDef("")
qdfNewID.Connect = SQLConnectString
qdfNewID.ReturnsRecords = True
qdfNewID.SQL = "sp_GetNewSegmentID"
Set rsNewID = qdfNewID.OpenRecordset
If Not IsNull(rsNewID!MaxOfSegmentID) Then
GetID = rsNewID!MaxOfSegmentID
Else
GetID = 0
End If
Else
GetID = 0
End If
DoCmd.Close acForm, "frmQuickAdd_AddNatural"
End Function
I had started to rewrite code but I have no clue if it is suppose to be like this at all.
Dim cnn As New ADODB.Connection
Dim rst As New ADODB.Recordset
cnn.Open "Provider=mssql;Data Source=" & dbq & ";User Id=" & uid & ";Password=" & pwd
With rst
.Open "SELECT COUNT(*) FROM " & tbl, cnn, adOpenKeyset, adLockOptimistic
num = .Fields(0)
.Close
End With
cnn.Close
Set rst = Nothing
Set cnn = Nothing
First, you really don’t want to introduce ADO into an application built and designed around DAO. Worse, is ADO has been on its way out for about 15 years now. In fact SQL server is dropping support for oleDB which ADO works on. (so don’t go there).
See this link about SQL server dropping oleDB support:
http://blogs.msdn.com/b/sqlnativeclient/archive/2011/08/29/microsoft-is-aligning-with-odbc-for-native-relational-data-access.aspx
The industry has moved away from ADO and all major vendors are suggesting to use Open Database Connectivity as the industry standard. (that means ODBC).
I would create and save a pass-though query in Access. You code can then be re-written as:
Public Function GetID(ByRef SegmentItem As String) As Long
Dim strSQL As String
strSQL = "sp_Insert('" & _
Left(Form_frmQuickAdd_AddNatural.txtSegmentDescription, 34) & "'," & _
"'" & CurrentUser & "', 0)"
With CurrentDb.QueryDefs("qryPass")
.SQL = strSQL
.ReturnsRecords = False
.Execute
End If
With CurrentDb.QueryDefs("qryPass")
.SQL = "sp_GetNewSegmentID"
.ReturnsRecords = True
GetID = Nz(.OpenRecordset()("MaxOfSegmentID"),0)
End With
End Function
So create one pass-though query. And you can use it quite much in all places where you were using JET-DIRECT. In access 2007, jet-direct support was dropped, but use of a simple pass-though query will more than suffice and also as the above shows save buckets of coding and developer time. If the “left” expression you have can return a null, then you likely need to wrap that expression in a nz() to return a “” (null string) or the appropriate value.

Create linked tables in access with vba

I'm working with a database in Access 2007, one front end and a few backends.
I like to convert all my linked tables to local for offline work so that i don't accidentally spoil the data.
I delete all the relations and tables and then run a saved import.
I'd like to recreate linked tables after I'm done with modifications. Problem is, Access won't save the import of linked tables. Is there a way to simply link all the tables from backend to frontend with VBA
I found this on the net that works, though slow
Function createAttached(strTable As String, strPath As String, strBaseTable As String) As Boolean
On Error GoTo CreateAttachedError
Dim tdf As TableDef
Dim strConnect As String
Dim fRetval As Boolean
Dim myDB As Database
DoCmd.SetWarnings False
Set myDB = CurrentDb
Set tdf = myDB.CreateTableDef(strTable)
With tdf
.Connect = ";DATABASE=" & strPath
.SourceTableName = strBaseTable
End With
myDB.TableDefs.Append tdf
fRetval = True
DoCmd.SetWarnings True
CreateAttachedExit:
createAttached = fRetval
Exit Function
CreateAttachedError:
If Err = 3110 Then
Resume CreateAttachedExit
Else
If Err = 3011 Then
Resume Next
End If
End If
End Function
https://www.microsoftaccessexpert.com/Microsoft-Access-Code-LinkTable.aspx

Referencing a linked table

I'm having an issue with my databases. I have multiple access databases that share the same pool of users and passwords. They reference the table of users and passwords by a linked table to the 'master' database (the backend that holds the tables for users and passwords). I also have a table that stores the current users and the databases that they are logged into. The problem I'm encountering is that my logout method is not actually logging them out. This is what it is roughly:
'this code is run on click of exit button
Public Sub logout(UserName As String, database As String)
On Error Resume Next
Dim dbMine As DAO.database
Set dbMine = CurrentDb
Dim qr As String
qr = "DELETE * FROM tblCurrentUsers WHERE username = '" & UserName & "' AND Database = '" & database & "' ;"
'debug.print qr
dbMine.Execute qr
Application.Quit
End Sub
The problem is, the records don't seem to be deleting. Do I need to set my database object to the source table instead of referencing the linked table that exists in the database on which the code is run? If so, do I just reference that database by relative path?
Add dbFailOnError option when executing query to catch the error details. See what error you get; that could help you in resolving of your issue.
Public Sub logout(UserName As String, database As String)
On Error GoTo mError:
Dim dbMine As DAO.database
Set dbMine = CurrentDb
Dim qr As String
qr = "DELETE * FROM tblCurrentUsers WHERE username = '" & UserName & "' AND Database = '" & database & "' ;"
'debug.print qr
dbMine.Execute qr, dbFailOnError
Application.Quit
Exit Sub
mError:
MsgBox "Error: " & Err.Description
End Sub
Try using
Set dbMine = DBEngine.Workspaces(0).Databases(0)
instead of
Set dbMine = CurrentDb
I've had similar issues when using CurrentDb. According to http://msdn.microsoft.com/en-us/library/office/bb237861(v=office.12).aspx,
The CurrentDb method creates another instance of the current database, while the DBEngine.Workspaces(0).Databases(0) syntax refers to the open copy of the current database.
Perhaps there's some subtle difference in the way DAO or Access handles the "new instance of the current database".

Refresh table link

I found the following function while browsing the Web that allows me to dynamically link a table to my Access database at the execution:
Function createAttached(strTable As String, strPath As String, strBaseTable As String) As Boolean
'************************************************************************************
'* Create an attached table in the current database from a table in a different MDB file.
'* In: *
'* strTable - name of linked table to create *
'* strPath - path and name of MDB file containing the table *
'* strBaseTable - name of table in strPath MDB *
'* Out: *
'* Return value: True/False, indicating success *
'* Modifies: *
'* Nothing, but adds a new table. *
'************************************************************************************
On Error GoTo CreateAttachedError
Dim tdf As TableDef
Dim strConnect As String
Dim fRetval As Boolean
Dim myDB As Database
DoCmd.SetWarnings False
Set myDB = CurrentDb
Set tdf = myDB.CreateTableDef(strTable)
With tdf
.Connect = ";DATABASE=" & strPath
.SourceTableName = strBaseTable
End With
myDB.TableDefs.Append tdf
fRetval = True
DoCmd.SetWarnings True
CreateAttachedExit:
createAttached = fRetval
Exit Function
CreateAttachedError:
If Err = 3110 Then
Resume CreateAttachedExit
Else
If Err = 3011 Then
Resume Next
End If
End If
End Function
This script works, however, if the table is already linked, it just does nothing (but an error event is still triggerered). I would like the same script to delete the linked table if it exists, or at least refresh that link so that the path is the right one. I have no idea of how to do this, it's probably quite simple but I don't know where to start.
Thank you.
Here's what I use. It also tests for if the table is a linked table before trying to refresh the link.
This code assumes that the db you're linking to is in the same folder as the db you're linking from. If not, remove the "Application.CurrentProject.Path" and add the appropiate path.
Public Sub RelinkTables()
Dim dbs As Database
Dim Tdf As TableDef
Dim Tdfs As TableDefs
Set dbs = CurrentDb
Set Tdfs = dbs.TableDefs
For Each Tdf In Tdfs
If Tdf.SourceTableName <> "" Then 'If the table source is other than a base table
Tdf.Connect = ";DATABASE=" & Application.CurrentProject.Path & "\filename.accdb" 'Set the new source
Tdf.RefreshLink 'Refresh the link
End If
Next 'Goto next table
End Sub