Following this question thread, was able to successfully code the change suggested by "changing the sourcetable of a linked table in access 2007 with C#". However, it appears this customer has queries coded with relationships defined at the query level and the delete/append process breaks the relationships. Anyone have any idea how to preserve the relationships? And why is it that the tabledef.Sourcetable can't be updated?
Code snip:
Option Compare Database
Sub test()
Dim tdf As TableDef
Dim db As Database
Set db = CurrentDb
Open "out.txt" For Output As #1
For Each tdf In db.TableDefs
If tdf.Connect <> vbNullString Then
Print #1, tdf.Name; " -- "; tdf.SourceTableName; " -- "; tdf.Connect
Select Case tdf.SourceTableName
Case "CTITLU.txt"
'tdf.SourceTableName = "dbo.GRANTSADJS"
'tdf.Connect = "ODBC;DRIVER=SQL Server;SERVER=DCFTDBCL01-L01\EDS_DEV;DATABASE=GRANTSDB2;UID=grants_reader;PWD=xxxxx;TABLE=DBO.GRANTSTABL"
tdf.RefreshLink
End Select
End If
Next
End Sub
When I run this with just the tdf.connect syntax uncommented, it errors on the tdf.refreshlink call with "Run-time error '3011': The Microsoft Access database engine could not find the object 'objectname'..." I'm trying to update a text linked table to the equivalent SQL Server based linked table. The objectname does have spaces and hyphens in it, but it is correctly showing the name in the error message. For whatever reason, the previous developer shipped dumps of the tables to a file system instead of linking the tables directly. This is a small DB with very light transactional activity so there is very little chance this will cause any issues. When the tdf.SourceTableName is uncommented, throws the "Run-time error '3268': Cannot set this property once the object is part of a collection."
I followed other threads indicating this issue (noted above), and was successful using the tdf.delete / tdf.append calls to duplicate the tabledef with new source tablename and connection info. However, the dependent query's relationship definitions have disappeared and the query is unusable without redefining all of the links.
C Perkins, thanks, that was it. There was a slight difference in the table definition such that when using delete/append, it 'broke' the relationships (yes joins) in the related query. Using a DB view to fix that, it worked just fine. However it still 'moves' the query from its former place as being related to the original table object. Our customers will at least have their current data and not a weekly snapshot. Thanks again.
Related
I have a macro that opens an ADODB connection to a MySQL library. There are two queries that are generated in the macro. The first, which works, is a select statement to check for duplicates. The second, which is not working, is to insert records onto the same table the select statement referenced. I am receiving no errors from VBA, and when I copy/paste directly into MySQL the query works fine.
At the top of my macro I set up the connection as follows:
TimesheetConn = "DRIVER={MySQL ODBC 5.3 ANSI Driver}; SERVER=*server number*;PORT=*port number*;database=my_db;UID=User;PWD=password;Option=2"
'Connection Info
Dim cnn As ADODB.Connection
On Error GoTo AdoError
Set cnn = New ADODB.Connection
With cnn
.ConnectionString = TimesheetConn
.Open
.CommandTimeout = 0
End With
Set FIT_Data = New ADODB.Recordset
Set Task_Data = New ADODB.Recordset
I then develop the Select query (fitidquery) and run it as:
FIT_Data.Open fitidquery, cnn, CursorType = 2
I do not close the connection, but then move on to generate my next query, an insert query (addtasks3), by looping through rows and assigning variables.
Then I try to call the connection again with:
Task_Data.Open addtasks3, cnn, adOpenForwardOnly, adLockReadOnly
cnn.Close
And it doesn't work, nor does it give me any errors either for VBA or SQL. As said before, copying the result of debug.print(addtasks3) into MySQL the query runs correctly and inserts the records.
I've tried opening a 2nd connection with the same parameters. That didn't work as well. I moved the On Error language down to in front of the second query call and it moves on to the AdoError message which seems to indicate that the connection is lost there.
I appreciate any help!
If you issue an Insert-statement, there is no need to involve a recordset. You can simply execute
cnn.Execute addtasks3
Or, if you want to get the number of rows you inserted, use a ADODB.Command:
Dim cmd As New ADODB.Command
cmd.ActiveConnection = cnn
cmd.CommandText = addtasks3
Dim rowsInserted As Long
Call cmd.Execute(rowsInserted)
Debug.Print rowsInserted & " rows inserted."
So I have to say thank you to all of you that mentioned On Errors. Your descriptions after rereading finally made it click with me what I did. I had thought that On Error Resume Next only worked for the line it was in front of in the code (I am still very much a VBA newbie learning as I go) and didn't realize that it would follow through the rest of the code. I had one before my first query because if its a new row it wouldn't return anything to check a duplicate against and cause an error. Hence why I wasn't getting error messages.
I added "On Error GoTo 0" after that section and now I'm getting error messages again. Turns out my user is denied use of the Insert Command for that table (even though I modeled it after a table where it does have use), so it looks like I just need to figure that out. Thank you everyone!
I have some VBA that programmatically updates the location of linked tables so that the front end of my database can be easily pointed at a different back end, without needing to click around in the Linked Table Manager. The technique is explained in this Microsoft blog post. This should be as simple as running the following code for each linked table in CurrentDb.TableDefs:
tableDef.Connect = ";DATABASE=" & newBackEndPath
tableDef.RefreshLink
However, when the table contains a multi-valued field, Access provides the following unhelpful and misleading error message:
Run-time error '3125':
'[table name]' is not a valid name. Make sure that it does not include invalid characters or punctuation and that it is not too long.
The same error appears when manually updating the links from the Linked Table Manager, although sometimes after a few attempts, the update will work without an error message. To work around the problem, I'm trying to refresh the link by creating a new TableDef and then renaming it over the old one:
Dim tdf As TableDef
Set tdf = CurrentDb.CreateTableDef(originalTableName & "_Copy")
tdf.Connect = ";DATABASE=" & newBackEndPath
tdf.SourceTableName = tableName
With CurrentDb.TableDefs
.Append tdf
.Refresh ' Required?
End With
RefreshDatabaseWindow ' Required?
DoEvents ' Required?
DoCmd.Rename originalTableNameName, acTable, originalTableNameName & "_Copy"
This usually works, but sometimes DoCmd.Rename throws the following run-time error:
Run-time error '7874':
Microsoft Access can't find the object '[table name]_Copy'.
If I enter the debugger, this usually triggers some kind of refresh and the _Copy table appears in the navigation pane. If I press F5 at this point, DoCmd.Rename executes with no problems. Clearly, there is some delay after the TableDef is appended before it can actually be found and renamed. I've added the TableDefs.Refresh, RefreshDatabaseWindow and DoEvents lines in an attempt to force the new table to appear, but they don't seem to help. Even forcing a delay of a few seconds doesn't seem to work, but somehow, opening the debugger does.
Is there any reliable way to refresh a linked table that contains a multi-valued field?
(I ignored the advice to avoid using multi-valued fields when I started this project, because I wanted users to be able to filter on multi-valued fields using the Quick Filters on a split form datasheet. This function is very helpful, but I'm beginning to wish I just built my own filtering UI after encountering this and numerous other bugs in the implementation of multi-valued fields.)
You could try a different approach altogether, by using DoCmd.TransferDatabase:
DoCmd.TransferDatabase acLink, "Microsoft Access", newBackEndPath, acTable, tableName, tableName
DoCmd.TransferDatabase is similar to using the GUI to link the table. It handles steps like refreshing the database pane for you. I usually recommend using TableDefs instead, but this situation might be an exception.
Of course, I support Sergey's advice to not use a multi-valued field. I'm a fan of the user experience of multi-valued fields and the associated comboboxes, but it has far-going consequences to use a MVF, like not being able to migrate your database to SQL server in the future
I have two Access (2016) databases on a company server. In database 'A' there is code that creates a linked table (in 'A') to a table in database 'B', performs some actions in database 'A' using the data in the linked table then deletes the linked table.
This has worked perfectly for several months but a couple of days ago it fell over.
On investigation I see that the linked table (in 'A') was not deleted, but the connection property of the table was reset to an empty string.
If I try to delete the linked table (in 'A') via the navigation pane I get the error 'Invalid use of Null'.
I've tried using code to update/restore the 'connection' property but that doesn't work.
I now have a table that I cannot delete.
Does anyone have any ideas how I can delete the table?
I've tried using code to update/restore the 'connection' property but that doesn't work.
I was able to recreate your issue by hacking an .accdb file and setting the .Connect property of a linked table to Null. Although ...
I was unable to delete the link (I got "Invalid use of Null", same as you), and
Compact and Repair Database did not fix the problem
... I was able to revive the link with the following VBA code:
Option Compare Database
Option Explicit
Sub ReviveBadLink()
Dim cdb As DAO.Database
Set cdb = CurrentDb
Dim tbd As DAO.TableDef
Set tbd = cdb.TableDefs("Clients")
tbd.Connect = ";Database=C:\Users\Public\Database1.accdb"
tbd.RefreshLink
End Sub
Once the link was revived I could then delete it as usual.
I've been asked to do some maintenance on an Access 2007 VBA database.
It has linked tables to another Access database in the same folder. It had hard-coded links to that database, so if the user copied the folder to a new folder, it tried to use the linked database in the original folder. They asked me to eliminate the danger of using the wrong linked database in that scenario.
I added code that runs when the database is opened, to make it reset the links to the database in it's own folder. If the linked database isn't there or was renamed, the user is prompted to browse to the correct database. So far so good.
But if the user cancels that dialog, I don't want to leave it connected to the wrong database. I want to set the linked tabledef's Connect property to the "correct" path even though the table is not there. Then the user will get an error that the linked table isn't there until they copy in the linked database -- rather than inadvertently use the wrong database.
When I use Resume Next to get past the error that is raised when I set the Connect property to a nonexistent database, the change doesn't stick, leaving it connected to the wrong database. So for now, I'm closing the database when that happens (after alerting the user that the linked database can't be found). That's safe in terms of not using the wrong database, but I don't think it's the ideal user experience.
So -- is it possible to set the Tabledef's Connect property to a nonexistent database?
Thanks,
Greg
is it possible to set the Tabledef's Connect property to a nonexistent database?
I don't think so. I recommend you delete all of the linked tables first, and then if there's no database to connect to, show an error saying so.
In order to relink the tables you will then need to have a local table in your frontend file that holds a list of all tables to be linked. Then you'll need to loop through that list every time you need to relink the tables, assuming that the links have all been deleted.
In order to update table links to render them invalid you need to
create a temporary copy of the back-end database (or perhaps a temporary copy of a "skeleton" back-end database with empty tables),
update the table links to point to the temporary database, and then
delete the temporary database.
For example, if I run the following code
Option Compare Database
Option Explicit
Sub MakeBadLink()
Const linkedTableName = "myLinkedTable" ' test value
Dim cdb As DAO.Database, tbd As DAO.TableDef
Dim tempFolder As String, linkedDb As String, tempDb As String
Dim fso As FileSystemObject
Set cdb = CurrentDb
linkedDb = Mid(cdb.TableDefs(linkedTableName).Connect, 11) ' remove ";DATABASE=" prefix
Set fso = New FileSystemObject
tempFolder = fso.GetSpecialFolder(TemporaryFolder) & "\"
tempDb = tempFolder & fso.GetFileName(linkedDb)
fso.CopyFile linkedDb, tempDb, True
Set tbd = cdb.TableDefs(linkedTableName)
tbd.Connect = ";DATABASE=" & tempDb
tbd.RefreshLink
Set tbd = Nothing
Set cdb = Nothing
fso.DeleteFile tempDb
Set fso = Nothing
End Sub
then any subsequent attempts to use [myLinkedTable] result in the error
Could not find file `C:\Users\Gord\AppData\Local\Temp\myDb.accdb'.
From the title, i know it seems like this has been answered too many times over, but I have a series of incomprehensible problems with it. This is also my first time asking for help through posts, so I might forget to mention some stuff.
Function update_location_id()
Dim rs As DAO.Recordset
Dim db As Database
Dim strSQL As String
Set db = CurrentDb
strSQL = "select id from location"
Set rs = db.OpenRecordset(strSQL)
MsgBox (rs.RecordCount)
End Function
I removed almost all the code from this function just to try to figure out why i couldn't get a record. this code generates a "too few parameters. expected one on the 'set rs = ...' line.
However if i change the select query's id to *, it works fine. However it returns 1. This is somewhat confusing seeing as there are a total of 3 records in the locations table right now.
Just incase its needed the location table looks like
id description
1 "Location 1"
2 "Location 2"
3 "Location 3"
This is causing me to pull my hairs out and i can't really move on with my project if i can't do such a basic database action as...getting information from it.
References: Visual Basic for Applications, Microsoft Access 14.0 Object Library, OLE Automation, Microsoft Office 14.0 Access database engine object, Microsoft Internet Controls.
Typically, the error "too few parameters" comes up when you use a column name that does not exist in the table, so it is interpreted as a parameter.
So, despite your given table data, I'd really check again the table name (location) and the column name (id).
As for the "1", yes, you have to insert
rs.MoveLast
rs.MoveFirst
after opening the recordset to get the correct RecordCount. Before moving to the last one, the recordset does not know how many records it yields (there are some more details to this problem, which I don't remember exactly right now, concerning recordset-type or so).