I am currently working on moving 100s of access databases from a variety of folders to another set of folders and need to update any references to linked tables that will be broken during the move. I have identified how to update the location of the linked database table by adding a macro to the access database itself by doing something like the following:
Dim tdf As TableDef, db As Database
Set db = CurrentDb
db.TableDefs.Refresh
For Each tdf In db.TableDefs
' My Logic for checking to see if it is is a linked
' table and then updating it appropriately
Next
Set collTables = Nothing
Set tdf = Nothing
Set db = Nothing
However, I do not want to have to add the code to each of the access databases so I was wondering if there was a way to create a VBS file which would execute the same type of logic. I tried the following code, but I am getting the following error when the line with the for each logic is executed: "Arguments are of the wrong type, are out of acceptable range or are in conflict with one another"
Set MyConn = CreateObject("ADODB.Connection")
MyConn.Open "Provider = Microsoft.Jet.OLEDB.4.0; Data Source = MyFile.mdb"
for each tblLoop in db.TableDefs
' business logic
next
set tblLoop = nothing
MyConn.close
set MyConn = nothing
I'm hoping that someone more familiar with doing this type of coding will be able to point me in the right direction. Is there a way to utilize the TableDefs table from outside of Access through a VBS file and if so, what would that code look like.
Thanks,
Jeremy
You cannot use tabledefs with ADO, but you can open the database in VBScript:
Dim db ''As DAO.Database
Dim ac ''As Access Application
''As noted by wmajors81, OpenDatabase is not a method of the application object
''OpenDatabase works with DBEngine: http://support.microsoft.com/kb/152400
Set ac = CreateObject("Access.Application")
ac.OpenCurrentDatabase("c:\test.mdb")
Set db = ac.CurrentDatabase
For Each tdf In db.TableDefs
Etc.
If you have start up code or forms, or database passwords, you will run into some problems, but these can be overcome, for the most part, by simulating the shift key press. This would be easier, I think, in VBA than VBScript, but AFAIK it is possible in VBScript. database passwords can be supplied in the OpenDatabase action.
I was able to expand upon the answer by #Remou to come up with some code that worked. Part of his answer included the following statement which threw an error "Set db = ac.OpenDatabase". As far as I can tell "OpenDatabase" is not a valid method, but OPenCurrentDatabase is. Also, I was getting an error when trying to set db equal to the value returned by OpenCurrentDatabase so I'm assuming that it is a sub and not a function. However, I was able to get access to the Current Database by utilizing ac.CurrentDB once I had established the connection to the the database utilizing OpenCurrentDatabase
Dim db ''As DAO.Database
Dim ac ''As Access Application
Set ac = CreateObject("Access.Application")
ac.OpenCurrentDatabase("D:\delete\UpdatingLinkedTableInAccess\GrpLfRsvs201108.mdb")
set db = ac.CurrentDB
For Each tdf In db.TableDefs
With tdf
If Len(.Connect) > 0 Then
If Left(.Connect, 4) = "ODBC" Then
' ignore these are connected via ODBC and are out of scope
Else
' biz logic
End If
End If
End With
next
set db = nothing
ac.Quit
set ac = nothing
Thanks again #Remou for your assistance.
You don't need to create an Access application instance. Use DBEngine and DAO.Workspace instead.
Option Explicit
Dim db
Dim dbe
Dim strDbPath
Dim tdf
Dim wrkJet
strDbPath = "C:\Access\webforums\whiteboard2003.mdb"
Set dbe = CreateObject("DAO.DBEngine.36")
Set wrkJet = dbe.CreateWorkspace("", "admin", "", 2) ' dbUseJet = 2
' exclusive = True and read-only = False '
Set db = wrkJet.OpenDatabase(strDbPath, True, False)
For Each tdf In db.TableDefs
If Left(tdf.Connect, 10) = ";DATABASE=" Then
WScript.Echo tdf.Connect
End If
Next
db.Close
Set db = Nothing
Set wrkJet = Nothing
Set dbe = Nothing
You would need "DAO.DBEngine.120" for ACCDB format database.
If you're using a database password, include it in OpenDatabase.
Set db = wrkJet.OpenDatabase(strDbPath, True, False, ";pwd=password")
Related
Is there any way I can make Access 2013 display created and modified date? Access 2003 used to display those features and can't seem to find any solution to Access 2013?
You can right-click the object list header, and do View -> Details. But that's still not a very good overview.
(Oh how I miss the Access 2003 database window...)
A better way is to query the MSysObjects table, e.g.:
SELECT MSysObjects.Type, MSysObjects.Name, MSysObjects.DateUpdate, MSysObjects.DateCreate
FROM MSysObjects
WHERE (((MSysObjects.Type)<>2 And (MSysObjects.Type)<>3 And (MSysObjects.Type)<>-32757)
AND ((Left([Name],1))<>'~') AND ((Left([Name],4))<>'Msys'))
ORDER BY MSysObjects.Type, MSysObjects.Name;
See here for the object type constants:
Meaning of MsysObjects values -32758, -32757 and 3 (Microsoft Access)
You may also be interested in this free "Database window replacement" add-in:
http://www.avenius.de/index.php?Produkte:DBC2007
If Access hasn't got a baked-in solution and you have a lot of objects to look at, you could always create your own with a table set up something like this:
And then write some VBA to loop through the object collections and write the properties you're interested in to the above table. The example below loops through the Tables and Queries collections, but you could write additional loops for Forms, Reports, etc. (There may even be a simpler way to just loop through all Access objects).
Public Sub CreatedModified()
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim tdf As DAO.TableDef
Dim qdf As DAO.QueryDef
Dim strSql As String
strSql = "DELETE * FROM tblCreatedModified"
Set db = CurrentDb
db.Execute strSql
Set rs = db.OpenRecordset("tblCreatedModified")
With rs
' tables
For Each tdf In db.TableDefs
If Not (tdf.Name Like "*MSys*" Or tdf.Name Like "~*") Then
.AddNew
!ObjectType = "Table"
!ObjectName = tdf.Name
!DateCreated = tdf.DateCreated
!DateModified = tdf.LastUpdated
.Update
End If
Next
' queries
For Each qdf In db.QueryDefs
If Not (qdf.Name Like "*MSys*" Or qdf.Name Like "~*") Then
.AddNew
!ObjectType = "Query"
!ObjectName = qdf.Name
!DateCreated = qdf.DateCreated
!DateModified = qdf.LastUpdated
.Update
End If
Next
End With
rs.Close
Set rs = Nothing
Set db = Nothing
End Sub
I'm trying to link two tables in different databases. What I've done is create a new table and then try to change the DESCRIPTION property to a path of a specific table in the other Database.
Set dbs = CurrentDb
thepath = "DATABASE=P:\Cadworx P&ID Implementation\3 Piping\P&IDs Jesus Test\Testproject\myTest.mdb;TABLE=Service"
Set tdf = dbs.TableDefs("ThisTable")
On Error Resume Next
tdf.Properties("Description") = "DATABASE=P:\Cadworx P&ID Implementation\3 Piping\P&IDs Jesus Test\Testproject\myTest.mdb;TABLE=Service"
If Err.Number = 3270 Then
Set prp = tdf.CreateProperty("Description", _
dbText, thepath)
tdf.Properties.Append prp
End If
This hasn't given me the desired results, as the link is never established. Can someone please tell me if this is the right way to do this or if there is a better way? Thanks for your help.
You can link to tables from other Access databases fairly easily using Access' in-built features.
Depending on the version of Access you're using, there should be an option to import/link to other data sources, including another Access database:
This starts an import/link wizard. Browse to the database file you want to link to:
Specify whether you want to import or link to data objects within the database file you browsed to (in this case, link):
Select the objects you want to link to:
Linked objects will appear in your navigation pane with a blue arrow denoting they are a linked object, rather than something stored locally to your current database:
You should then be able to use your linked tables as if they were any other kind of table in your database.
Here is the solution to the problem.
Dim dbs As DAO.Database
Dim tdf As DAO.TableDef
Dim strDbFile As String
Dim strConnect As String
Dim strSourceTableName As String
Dim strLinkName As String
Dim f As Object
Dim varfile As Variant
Set f = Application.FileDialog(3) 'Windows Explorer, 3 means file picker. 4 Is for folder, 1 to open dialog box, 2 to save as
f.show 'Show Windows explorer
With f
f.allowmultiselect = False
For Each varfile In .selecteditems
strDbFile = varfile 'File selected assigned to source database file
Next
End With
strSourceTableName = "Service" 'Source Table
strLinkName = "Service" 'Final table name once link has been established
strConnect = "MS Access;PWD=" & strPassword & ";DATABASE=" & strDbFile
Set dbs = CurrentDb
Set tdf = dbs.CreateTableDef
tdf.Connect = strConnect 'Establish link between databases
tdf.SourceTableName = strSourceTableName
tdf.Name = strLinkName
dbs.TableDefs.Append tdf
As Gustav said, the solution was along what he posted. New to access so I didn't really know what the DAO library is. Thanks for your help anyway!
I have a simple_table with 4 fields:
a,b,x,P
I am trying to update the field p based on the output of a function that uses the other fields as input parameters. In this case the function is an excel function.
I was using SQL server but really need to access some statistical functions. So yesterday I opened access for the first time. Eeek. I've spent the last day trying to learn vba and following various tutorials on recordsets.
The bit I'm struggling with is how to I update a the P field based on the other fields? In a loop?
Thanks very much.
Dim objExcel As Excel.Application
Set objExcel = CreateObject("Excel.Application")
'Test it works
MsgBox objExcel.Application.BetaDist(0.4, 2, 5)
'OK, that works :)
'set up the ADO stuff
Dim cnn1 As ADODB.Connection
Dim MyRecordSet As New ADODB.Recordset
Set cnn1 = CurrentProject.Connection
MyRecordSet.ActiveConnection = cnn1
'Load data into MyRecordSet
MySQLcmd = "SELECT * FROM simple_table"
MyRecordSet.Open MySQLcmd
'HELP WITH THE NEXT BIT PLEASE!
'Some kind of loop to go through the recordset to set the field P
' equal to the result of the excel function betadist(x,a,b)
'I imagine looping through something like the following semi pseudo code ???
myRecordSet.Fields(“P”).Value = objExcel.Application.BetaDist(myRecordSet.Fields(“x”).Value, myRecordSet.Fields(“a”).Value, myRecordSet.Fields(“b”).Value)
'end of the loop
objExcel.Quit
Set objExcel = Nothing
MyRecordSet.Close
cnn1.Close
Set MyRecordSet = Nothing
Set cnn1 = Nothing
Since your code works with "Dim objExcel As Excel.Application", that means you have a reference set for the Excel object library. In that case, you don't need a full Excel application instance in order to use the BetaDist function. You can set an object variable to Excel.WorksheetFunction and call the function as a method of that object. However, I don't know whether that makes a significant difference. I didn't test the CreateObject("Excel.Application") alternative.
In this sample, I used a DAO recordset instead of ADO. The reason is I've found DAO can be significantly faster with native Access (Jet/ACE) data sources. You can switch to ADO if you prefer, but I don't see an advantage.
Notice I opened the table directly rather than via a query. The DAO dbOpenTable option can also benefit performance.
With those details out of the way, it's just a simple matter of looping through the recordset, calling the function with values from the current row, and storing the function's result in the P field ... pretty much what you outlined in your pseudo-code. :-)
Dim objWFunction As Object ' Excel.WorksheetFunction
Dim MyRecordSet As DAO.Recordset
Dim db As DAO.database
Set objWFunction = Excel.WorksheetFunction ' Excel reference required
Set db = CurrentDb
Set MyRecordSet = db.OpenRecordset("simple_table", dbOpenTable)
With MyRecordSet
Do While Not .EOF
'Debug.Print objWFunction.BetaDist(!x, !a, !b)
.Edit
!p = objWFunction.BetaDist(!x, !a, !b)
.Update
.MoveNext
Loop
.Close
End With
Set MyRecordSet = Nothing
Set db = Nothing
Set objWFunction = Nothing
I actually have 2 questions:
1. How might I see who is using my Access database?
E.g: There is someone with an Access database opened and it created the .ldb file, I would like to see a list of who opened that database (it could be more than one person).
2. How might I see who is using a linked table?
E.g: I have 10 different Access databases, and all of them are using a same linked table. I would like to see who is using that linked table.
I don't even know if it's really possible, but I really appreciate your help!
For you information: The main problem is that lots of people use the same Access in the same network drive, so when I need to change it I have to kick them all out, but I never know who is actually using it.
Update: Rather than reading and parsing the .ldb/.lacdb file, a better approach would be to use the "User Roster" feature of the Access OLEDB provider as described in the Knowledge Base article
https://support.microsoft.com/en-us/kb/285822
and in the other SO question
Get contents of laccdb file through VBA
Original answer:
I put together the following a while ago. It looked promising but then I discovered that computers are not immediately removed from the lock file when they disconnect. Instead, Jet/ACE seems to (internally) mark them as inactive: If ComputerA disconnects and then ComputerB connects, ComputerB overwrites ComputerA's entry in the lock file.
Still, it does provide a list of sorts. I'm posting it here in case somebody can offer some suggestions for refinement.
I created two tables in my back-end database:
Table: [CurrentConnections]
computerName Text(255), Primary Key
Table: [ConnectionLog]
computerName Text(255), Primary Key
userName Text(255)
A VBA Module in my back-end database contained the following code to read (a copy of) the lock file and update the [CurrentConnections] table:
Public Sub GetCurrentlyConnectedMachines()
Dim cdb As DAO.Database, rst As DAO.Recordset
Dim fso As Object '' FileSystemObject
Dim lck As Object '' ADODB.Stream
Dim lockFileSpec As String, lockFileExt As String, tempFileSpec As String
Dim buffer() As Byte
Set cdb = CurrentDb
cdb.Execute "DELETE FROM CurrentConnections", dbFailOnError
Set rst = cdb.OpenRecordset("SELECT computerName FROM CurrentConnections", dbOpenDynaset)
lockFileSpec = Application.CurrentDb.Name
If Right(lockFileSpec, 6) = ".accdb" Then
lockFileExt = ".laccdb"
Else
lockFileExt = ".ldb"
End If
lockFileSpec = Left(lockFileSpec, InStrRev(lockFileSpec, ".", -1, vbBinaryCompare) - 1) & lockFileExt
'' ADODB.Stream cannot open the lock file in-place, so copy it to %TEMP%
Set fso = CreateObject("Scripting.FileSystemObject") '' New FileSystemObject
tempFileSpec = fso.GetSpecialFolder(2) & "\" & fso.GetTempName
fso.CopyFile lockFileSpec, tempFileSpec, True
Set lck = CreateObject("ADODB.Stream") '' New ADODB.Stream
lck.Type = 1 '' adTypeBinary
lck.Open
lck.LoadFromFile tempFileSpec
Do While Not lck.EOS
buffer = lck.Read(32)
rst.AddNew
rst!computerName = DecodeSZ(buffer)
rst.Update
buffer = lck.Read(32) '' skip accessUserId, (almost) always "Admin"
Loop
lck.Close
Set lck = Nothing
rst.Close
Set rst = Nothing
Set cdb = Nothing
fso.DeleteFile tempFileSpec
Set fso = Nothing
End Sub
Private Function DecodeSZ(buf() As Byte) As String
Dim b As Variant, rt As String
rt = ""
For Each b In buf
If b = 0 Then
Exit For '' null terminates the string
End If
rt = rt & Chr(b)
Next
DecodeSZ = rt
End Function
The following code in the Main_Menu form of the front-end database updated the [ConnectionLog] table
Private Sub Form_Load()
Dim cdb As DAO.Database, rst As DAO.Recordset
Dim wshNet As Object '' WshNetwork
Set wshNet = CreateObject("Wscript.Network")
Set cdb = CurrentDb
Set rst = cdb.OpenRecordset("SELECT * FROM ConnectionLog", dbOpenDynaset)
rst.FindFirst "ComputerName=""" & wshNet.computerName & """"
If rst.NoMatch Then
rst.AddNew
rst!computerName = wshNet.computerName
Else
rst.Edit
End If
rst!userName = wshNet.userName
rst.Update
Set wshNet = Nothing
End Sub
Finally, the following form in the back-end database listed [its best guess at] the current connections
It is a "continuous forms" form whose Record Source is
SELECT CurrentConnections.computerName, ConnectionLog.userName
FROM CurrentConnections LEFT JOIN ConnectionLog
ON CurrentConnections.computerName = ConnectionLog.computerName
ORDER BY ConnectionLog.userName;
and the code-behind is simply
Private Sub Form_Load()
UpdateFormData
End Sub
Private Sub cmdRefresh_Click()
UpdateFormData
End Sub
Private Sub UpdateFormData()
GetCurrentlyConnectedMachines
Me.Requery
End Sub
Easy. Open the .ldb file in notepad (or any text editor) and you can see the machine names.
RE: How might I see who is using my Access database?
•E.g: There is someone with an Access database opened and it created the .ldb file, I would like to see a list of who opened that database (it could be more than one person).
Just happened across this while looking for something else, and I thought I might share what I do for this. Note that this assumes that the host computer (the computer on which the database file resides) uses file sharing to provide access to the file.
You will need to be on the host computer, or have authority to connect to that machine.
click Start
right-click My Computer and select Manage
if you're not on the host computer, right-click 'Computer Management' and enter the host's name
Expand 'Shared Folders' and click on 'Open Files'
At the right is the list of currently open files with the username for each current user
I agree with Gord's Original answer. I used this code on my database, it seems that there is a way around computers not being taken out of CurrentConnections upon exit of the DB.
I placed this on my main menu form because it is always open until the user exits. I used the unload event on my form to get this to work, and it works awesome! Here is my code
p.s. Ignore SetWarnings I just have that on so the user doesn't have to click through prompts.
Private Sub Form_Unload(Cancel As Integer)
Dim wshNet As Object
Dim deleteSQL As String
Set wshNet = CreateObject("WScript.Network")
DoCmd.SetWarnings False
deleteSQL = "DELETE tblCurrentConnections.* " & _
"FROM tblCurrentConnections WHERE[computerName] = '" & wshNet.computerName & "';"
DoCmd.RunSQL deleteSQL
DoCmd.SetWarnings True
End Sub
Summary: why might Docmd.Rename on a table result in tables that don't change name over a Link from another DB?
I'm trying to fix up an old database that needs TLC. Part of this is deleting lots of unused cruft, amongst which are some tables. The first part if a VBA procedure that calls DoCmd.Rename on these tables, renaming with DELETE_ prepended.
The "deletes" appear to work fine - but when I try to reference tables from another DB using the Linked Table manager, no renames have happened at all. If I go back and load that DB, the table names are changed.
Is it best to use TableDefs().Name to rename? Is that a better method? I'd assumed an "official" way like Rename would be better.
I'm using Access 2007 on Win7/64. The files are in MDB format.
Do you wish to rename the tables in the linked database? If so, you can use OpenDatabase to reference the linked Access database. You might try something on the lines of:
Dim dbLink As DAO.Database
Dim dbCurr As DAO.Database
Dim ws As DAO.Workspace
Dim rst As DAO.Recordset
Dim tdf As TableDef
Set ws = DBEngine.Workspaces(0)
Set dbCurr = CurrentDb
For Each tdf In dbCurr.TableDefs
sConn = tdf.Connect
sSource = tdf.SourceTableName
sName = tdf.Name
If InStr(sConn, ";DATABASE=") > 0 Then
strdb = Mid(sConn, InStr(sConn, ";DATABASE=") + 10)
If InStr(sConn, "PWD") > 0 Then
sPWD = Left(sConn, InStr(sConn, ";DATABASE="))
Else
sPWD = vbNullString
End If
If Dir(strdb) <> vbNullString Then
Set dbLink = ws.OpenDatabase(strdb, False, False, sPWD)
dbLink.TableDefs(sSource).Name = "DELETE_" & sSource
End If
End If
Next