Linking tables in Access - ms-access

I have an access database that links to 6 tables. These tables are updated weekly and kept in a folder with the date. I would like for my access program to ask the user to select the location of the tables with out specifically using the Linked Table Manager.

The following code will prompt a user for the full path and file name of the database to be linked to. I decided to do this rather than just prompt for a folder. I strongly suggest you look at the connect string for one of your linked tables and make sure no other parameters are specified other than something like ';DATABASE=C:\Foldera\YYMMDD\MyAccessDB.mdb"
Private Function ReLinkTables()
Dim dbs As DAO.Database
Dim tdf As DAO.TableDef
Dim tdf2 As DAO.TableDef
Dim strConn As String
Dim strNewPath As String
Dim strTableName As String
On Error GoTo ERROR_HANDLER
' Prompt user for new path...
strNewPath = GetFolder
' Exit if none
If strNewPath = "" Then
Exit Function
End If
Set dbs = CurrentDb
dbs.TableDefs.Refresh
' Find all the linked tables...
For Each tdf In dbs.TableDefs
'Debug.Print tdf.Name & vbTab & tdf.Connect
If Len(tdf.Connect) > 0 Then
strTableName = tdf.Name
Debug.Print "Linked Table: " & tdf.Name & vbTab & tdf.Connect
dbs.TableDefs.Delete strTableName ' Delete the linked table
strConn = ";DATABASE=" & strNewPath
Set tdf2 = CurrentDb.CreateTableDef(strTableName, dbAttachSavePWD, strTableName, strConn)
CurrentDb.TableDefs.Append tdf2
Else ' Not a linked table
'Debug.Print "Keep: " & tdf.Name & vbTab & tdf.Connect
End If
Next tdf
Set tdf = Nothing
Set tdf2 = Nothing
dbs.TableDefs.Refresh
dbs.Close
Set dbs = Nothing
MsgBox "Finished Relinking Tables"
Proc_Exit:
Exit Function
ERROR_HANDLER:
Debug.Print Err.Number & vbTab & Err.Description
Err.Source = "Module_Load_SQLSERVER_DATABASE: ReLinkTables at Line: " & Erl
If Err.Number = 9999 Then
Resume Next
End If
MsgBox Err.Number & vbCrLf & Err.Description
Resume Proc_Exit
Resume Next
End Function
Function GetFolder() As String
Dim fldr As FileDialog
Dim sItem As String
Set fldr = Application.FileDialog(msoFileDialogFilePicker)
With fldr
.Title = "Select a Folder"
.AllowMultiSelect = False
'.InitialFileName = "Z:\xxxxxxxx" ' You can change to valid start path
If .Show <> -1 Then GoTo NextCode
sItem = .SelectedItems(1)
End With
NextCode:
Debug.Print "User selected path: >" & sItem & "<"
If sItem = "" Then MsgBox "User did not select a path.", vbOKOnly, "No Path"
GetFolder = sItem
Set fldr = Nothing
End Function

Related

How to check if the table is empty in Access 2003?

I need only empty tables in access database. Additionally, it would be great if I can get empty tables from list of tables that I have (part of all tables). But listing all empty tables would work also.
You can use a small VBA function that checks this. Something like:
Function fIsTableEmpty(strTableName As String) As Boolean
On Error GoTo E_Handle
Dim db As DAO.Database
Dim rsData As DAO.Recordset
Dim strSQL As String
Set db = CurrentDb
strSQL = "SELECT COUNT(*) FROM [" & strTableName & "];"
Set rsData = db.OpenRecordset(strSQL)
fIsTableEmpty = True ' start by assuming that there are records
If Not (rsData.BOF And rsData.EOF) Then
If rsData(0) > 0 Then fIsTableEmpty = False
End If
fExit:
On Error Resume Next
rsData.Close
Set rsData = Nothing
Set db = Nothing
Exit Function
E_Handle:
MsgBox Err.Description & vbCrLf & vbCrLf & "fIsTableEmpty", vbOKOnly + vbCritical, "Error: " & Err.Number
Resume fExit
End Function
You can use DCount:
Public Function ListEmptyTables()
Dim Table As DAO.TableDef
For Each Table In CurrentDb.TableDefs
If Table.SourceTableName = "" Then
If DCount("*", Table.Name) = 0 Then
Debug.Print Table.Name
End If
End If
Next
End Function

VBA Code to convert rows into PDF files start to print blank pages after sometime

I wrote a VBA code to print every row of my table into pdf files while creating directories for them.
At first it look great, it doesn't show any kind of error but when the loop ends (around 1200 rows) if I go check the files created, some worked perfectly while others are just blank pages.
Any idea why this might be happening?
Option Compare Database
Private Sub Command0_Click()
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim ps As DAO.Recordset
Dim MyFileName As String
Dim mypath As String
Dim temp As String
'mypath = "C:\Docs\"
Set db = CurrentDb()
Set rs = db.OpenRecordset("SELECT * FROM [TABLE]", dbOpenSnapshot)
Do While Not rs.EOF
On Error Resume Next
b = "C:\Docs\" & rs("ENTERPRISE")
MkDir (b)
b1 = "C:\Docs\" & rs("ENTERPRISE") & "\" & "ECONOMICS"
MkDir (b1)
b2 = "C:\Docs\" & rs("ENTERPRISE") & "\" & "ECONOMICS" & "\" & Year(rs("DATE")) & "-" & Month(rs("DATE"))
MkDir (b2)
a = b2 & "\" & rs("NUM") & "-" & rs("ITEM")
MkDir (a)
mypath = a & "\"
temp = rs("DOC_LANC")
MyFileName = rs("NUM") & rs("ITEM") & ".PDF"
DoCmd.OpenReport "PDF", acViewReport, , "[DOC_LANC]='" & temp & "'"
DoCmd.OutputTo acOutputReport, "", acFormatPDF, mypath & MyFileName
DoCmd.Close acReport, "PDF"
DoEvents
rs.MoveNext
Loop
rs.Close
Set rs = Nothing
Set db = Nothing
End Sub

Fix link on-the-fly as an Error handle for error 3044 or more

I have a massive set of linked databases that have the potential to move. Luckily they are all in ONE working directory of nested folders.
I have effectively created a module that has the path of this working folder defined.
As strWorkingFolder
Now the VBA of the main control center remains intact for multiple calls running and executing queries (append, delete, insert) etc. EXCEPT each of the databases that are still linked to the old folder.
I figured that whenever the error 3044 (Not sure of the exact verbiage "The path to this table does not exist), I could just relink to the correct path - because it is known: It would be strWorkingFolder (concatenated to whatever nested folder the database is in)
I thought I could get away with just linked tables, but apparently, I will need to re-link all kinds of files: csv, Excel, as well as ACCDB.
How can I get it to work?
This is currently what I have setup
Sub RemoveLinks()
Dim tdf As TableDef
For Each tdf In CurrentDb.TableDefs
If Left(tdf.Name, 4) <> "MSys" And (tdf.Attributes And dbAttachedTable) = dbAttachedTable Then
CurrentDb.TableDefs.Delete tdf.Name
End If
Next tdf
Set tdf = Nothing
End Sub
Sub LinkDatabase(StrDBPath As String)
Dim dbs As Database
Dim tdf As TableDef
Set dbs = OpenDatabase(StrDBPath)
For Each tdf In dbs.TableDefs
If Left(tdf.Name, 4) <> "MSys" Then
DoCmd.TransferDatabase acLink, "Microsoft Access", Trim(StrDBPath), acTable, tdf.Name, tdf.Name
SysCmd acSysCmdSetStatus, "Processing table [" & tdf.Name & "]..."
End If
Next tdf
SysCmd acSysCmdClearStatus
Set dbs = Nothing
Set tdf = Nothing
End Sub
Sub RefreshLinks(StrDBPath As String)
Dim tdf As TableDef
For Each tdf In CurrentDb.TableDefs
If (tdf.Attributes And dbAttachedTable) = dbAttachedTable Then
tdf.Connect = "; Database = " & StrDBPath
SysCmd acSysCmdSetStatus, "Processing table [" & tdf.Name & "]..."
tdf.RefreshLink
End If
Next tdf
Set tdf = Nothing
SysCmd acSysCmdClearStatus
End Sub
And finally, in the error_handler, I will trap 3044 and call
Public Sub Relink(strEnginePath)
Dim dbs As Database
Set dbs = CurrentDb
RemoveLinks
LinkDatabase (strEnginePath)
RefreshLinks (strEnginePath)
End Sub
Is there a better way to go about this?
I have altered your code so it will handle text and Excel in addition to Access tables. If you have other types attached, you need to modify the code.
NOTE: With this code, you should NOT delete the links because that will remove all of the attributes you need!
Also, if you have any parameters following the path/file names in the connect strings, you need to add code to retain that information. I hope you have some standards in place that would allow some logical actions to be taken.
Sub RefreshLinks(StrDBPath As String)
Dim iLen As Integer
Dim iStart As Integer
Dim iEnd As Integer
Dim iPos As Integer
Dim strOldConn As String
Dim strNewConn As String
Dim strFile As String
Dim tdf As TableDef
On Error GoTo Error_Trap
For Each tdf In CurrentDb.TableDefs
If (tdf.Attributes And dbAttachedTable) = dbAttachedTable Then
Debug.Print "Table Name: " & tdf.Name
strOldConn = tdf.Connect ' Save the connect string
iLen = Len(strOldConn)
iStart = InStr(1, strOldConn, "DATABASE=") ' Find start of path
iEnd = InStr(iStart + 1, strOldConn, ";") ' Is there more after path?
Debug.Print tdf.Name & ": " & tdf.Connect
If LCase(left(strOldConn, 4)) = "text" Then ' Text file attached
strNewConn = left(strOldConn, iStart + 8) & StrDBPath
ElseIf LCase(left(strOldConn, 5)) = "excel" Then ' Excel file attached
strFile = ""
For iPos = iLen To 1 Step -1 ' Get the file name from the path
If Mid(strOldConn, iPos, 1) = "\" Then Exit For
strFile = Mid(strOldConn, iPos, 1) & strFile
Next
If iPos = 0 Then
MsgBox "Did not find path delimiter '\'" & vbCrLf & vbCrLf & "for TDF '" & tdf.Name & "'", vbOKOnly + vbCritical, "Path Delimiter Unknown"
End If
strNewConn = left(strOldConn, iStart + 8) & StrDBPath & "\" & strFile
Else
' Assume it is Access table. If other types, add code to handle.
strFile = ""
For iPos = iLen To 1 Step -1 ' Get the file name from the path
If Mid(strOldConn, iPos, 1) = "\" Then Exit For
strFile = Mid(strOldConn, iPos, 1) & strFile
Next
If iPos = 0 Then
MsgBox "Did not find path delimiter '\' in connect string '" & strOldConn & "'", vbOKOnly + vbCritical, "Wrong delimiter?"
End If
strNewConn = left(strOldConn, iStart + 8) & StrDBPath & "\" & strFile
End If
Debug.Print " (new): " & strNewConn
tdf.Connect = strNewConn
SysCmd acSysCmdSetStatus, "Processing table [" & tdf.Name & "]..."
tdf.RefreshLink
Else
' Ignore this table since it is not linked.
End If
Next tdf
Set tdf = Nothing
SysCmd acSysCmdClearStatus
Exit Sub
Error_Trap:
MsgBox "Error: " & Err.Number & vbTab & Err.Description & vbCrLf & vbCrLf & _
"While processing table: " & tdf.Name & vbCrLf & _
"Old: " & strOldConn & vbCrLf & _
"New: " & strNewConn, vbOKOnly, "Relink Error"
Exit Sub
End Sub

Is there a faster alternative to DLookup?

I'm using DLookup to search for a field in a table. It runs correctly, but is slow. Is there anything I can do to speed it up?
Here's my existing code:
Private Sub cmdLogin_Click()
strUserLevel = ""
If IsNull(Me.cmbUserName) Or Me.cmbUserName = "" Then
MsgBox "You must enter a User Name.", vbOKOnly, "Required Data"
Me.cmbUserName.SetFocus
Exit Sub
End If
If IsNull(Me.txtPassword) Or Me.txtPassword = "" Then
MsgBox "You must enter a Password.", vbOKOnly, "Required Data"
Me.txtPassword.SetFocus
Exit Sub
End If
'strUserName = cmbUserName.Value
If Me.txtPassword.Value = DLookup("Password", "tableUser", "[lngEmpID]=" & Me.cmbUserName.Value) Then
lngMyEmpID = Me.cmbUserName.Value
strUserLevel = DLookup("Department", "tableUser", "[lngEmpID]=" & Me.cmbUserName.Value)
strUserName = DLookup("User_Name", "tableUser", "[lngEmpID]=" & Me.cmbUserName.Value)
boolInventoryMDL = DLookup("Inventory", "tableDepartment", "[Department]=""" & strUserLevel & """")
boolDispositionMDL = DLookup("Disposition", "tableDepartment", "[Department]=""" & strUserLevel & """")
boolReviewCloseMDL = DLookup("Review", "tableDepartment", "[Department]=""" & strUserLevel & """")
boolAdministratorMDL = DLookup("Administrator", "tableDepartment", "[Department]=""" & strUserLevel & """")
boolUserListMDL = DLookup("UserList", "tableDepartment", "[Department]=""" & strUserLevel & """")
boolUserLevelMDL = DLookup("UserLevel", "tableDepartment", "[Department]=""" & strUserLevel & """")
If strUserLevel = "Superuser" Then
MsgBox "Welcome back Superuser! You can access all the modules here..", vbOKOnly, "Caution"
Else
MsgBox "Welcome! Login Success!", vbOKOnly, "Login Page"
End If
DoCmd.Close acForm, "frmLogin", acSaveNo
DoCmd.OpenForm "frmModule"
Else
MsgBox "Password Invalid. Please Try Again", vbOKOnly, "Invalid Entry!"
Me.txtPassword.Value = ""
Me.txtPassword.SetFocus
End If
End Sub
I don't believe the problem is due to inherent slowness of DLookup. Rather the problem is that the code uses so many of them.
Open one recordset based on a query of tableUser and take the values you need from that recordset. Then open a second recordset from a query of tableDepartment and get your remaining values.
Dim db As DAO.database
Dim qdf As DAO.QueryDef
Dim rs As DAO.Recordset
Dim strSelect As String
strSelect = "SELECT u.Password, u.Department, u.User_Name" & vbCrLf & _
"FROM tableUser AS u WHERE u.lngEmpID = [which_EmpId];"
Set db = CurrentDb
Set qdf = db.CreateQueryDef(vbNullString, strSelect)
qdf.Parameters("which_EmpId") = Me.cmbUserName
Set rs = qdf.OpenRecordset(dbOpenSnapshot)
If Not rs.EOF Then
If rs![Password] = Me.txtPassword Then
strUserLevel = rs!Department
strUserName = rs!User_Name
rs.Close
' open another recordset from a query of tableDepartment
' to retrieve your bool????? values
End If
End If
In that abbreviated sample, I used a temporary QueryDef for the parameterized SELECT query. However you would be better of to save that SQL as a named query, perhaps qryFetchUserData. Then at run time, instead of recreating the query each time, you could simply open the saved query.
Set qdf = db.QueryDefs("qryFetchUserData")
For optimum performance, you should add indexes on tableUser.lngEmpID and tableDepartment.Department if they're not already indexed.

Re-Link to new mdb then delete old database (mdb)

I have a procedure where the ultimate objective is to update all tables on a server backend database from a laptop. Once this is complete, I want to delete the local (laptop) mdb and replace the deleted file (mdb) with the server mdb.
All seems to work well except I can't delete the local version even though I have re-linked the laptop front end to the server backend. Here is my code:
Call CloseALLFormsReports
Call RelinkTables("K:\Proposals\Northway\Data\Northway Data.accdb")
******************************************
'backup current c: database
tBackupfile = "C:\Proposals\backup\Northway DATA" & Format(Now(), "yyyymmdd hhmm") & ".accdb"
Call TransferBEData("C:\Proposals\Northway DATA.accdb", tBackupfile)
'now overwrite c:drive file
Call TransferBEData("K:\Proposals\Northway\Data\Northway Data.accdb", "C:\Proposals\Northway DATA.accdb")
Call RelinkTables("C:\Proposals\Northway DATA.accdb")
*************HERE IS THE TransferBEDate function:
Function TransferBEData(ByVal tSource As String, ByVal tDestination As String)
If FileExists(tDestination) Then
Kill tDestination
End If
FileCopy tSource, tDestination
End Function
************HERE IS MY Relinking Function
Public Sub RelinkTables(strNewPath As String)
Dim dbs As DAO.Database
Dim tdf As TableDef
Dim intCount As Integer
Dim frmCurrentForm As Form
Dim relink As Boolean
DoCmd.Hourglass True
On Error GoTo ErrLinkUpExit
'Me.lblMsg.Visible = True
'Me.cmdOK.Enabled = False
Set dbs = CurrentDb
For intCount = 0 To dbs.TableDefs.Count - 1
Set tdf = dbs.TableDefs(intCount)
If tdf.Connect <> "" Then
'Me.lblMsg.Caption = "Refreshing " & tdf.Name
DoEvents
tdf.Connect = ";DATABASE=" & strNewPath
tdf.RefreshLink
End If ' tdf.Connect <> ""
Next intCount
Set dbs = Nothing
Set tdf = Nothing
DoCmd.Hourglass False
MsgBox ("The file: " & strNewPath & " was successfully linked.")
'Me.lblMsg.Caption = "All Links were refreshed!"
relink = True
'Me.cmdOK.Enabled = True
Exit Sub
ErrLinkUpExit:
DoCmd.Hourglass False
Select Case Err
Case 3031 ' Password Protected
MsgBox "Back End '" & strNewPath & "'" & " is password protected"
Case 3011 ' Table missing
DoCmd.Hourglass False
MsgBox "Back End does not contain required table '" & _
tdf.SourceTableName & "'"
Case 3024 ' Back End not found
MsgBox "Back End Database '" & strNewPath & "'" & " " & _
"Not Found"
Case 3051 ' Access Denied
MsgBox "Access to '" & strNewPath & "' Denied " & _
vbCrLf & _
" May be Network Security or Read Only Database"
Case 3027 ' Read Only
MsgBox "Back End '" & strNewPath & "'" & " is Read " & _
"Only "
Case 3044 ' Invalid Path
MsgBox strNewPath & " Is Not a Valid Path"
Case 3265
MsgBox "Table '" & tdf.Name & "'" & _
" Not Found in ' " & strNewPath & "'"
Case 3321 ' Nothing Entered
MsgBox "No Database Name Entered"
Case Else
MsgBox "Uncaptured Error " & Str(Err) & " " & _
Err.Description
End Select
Set tdf = Nothing
relink = False
'******************Get rid of blank records
DoCmd.SetWarnings False
DoCmd.OpenQuery "Delete_Blank_Material_Records"
DoCmd.SetWarnings True
'********************************************
End Sub
Function TransferBEData(ByVal tSource As String, ByVal tDestination As String)
If FileExists(tDestination) Then
Kill tDestination
End If
FileCopy tSource, tDestination
End Function
The reason this doesn't work, is because re-linking the tables to another source will not delete the entry from the .mdw lock file (or security equivalent in later versions than 03). You would need to close your front-end database and then re-open in order to unlock the local .mdb file.