How to delete a table if it exists? - vba

I use MS Access macros and queries to build my application. I use some temporary import files, and need to either run a macro, or some VBA, to test if they exist, and then if they do, to delete them.
My table name is "TempImport1"
I've researched this via Google searches and have some VBA that might work. I have cut/pasted VBA code under a button in the past, and it worked, but not this time. How do I put the code into a module or a click sub button?
Function IsTable(sTblName As String) As Boolean
'does table exists and work ?
'note: finding the name in the TableDefs collection is not enough,
' since the backend might be invalid or missing
On Error GoTo TrapError
Dim x
x = DCount("*", sTblName)
IsTable = True
Exit Function
TrapError:
Debug.Print Now, sTblName, Err.Number, Err.Description
IsTable = False
End Function

First you should check whether the table exists and then you should try to close it, if it exists. Then you should set warnings to False, so it does not ask you whether you are sure that you want to delete the table.
In the example below, you delete Table3. The If Not IsNull is checking whether the table exists:
Option Compare Database
Option Explicit
Public Sub DeleteIfExists()
Dim tableName As String
tableName = "Table3"
If Not IsNull(DLookup("Name", "MSysObjects", "Name='" & tableName & "'")) Then
DoCmd.SetWarnings False
DoCmd.Close acTable, tableName, acSaveYes
DoCmd.DeleteObject acTable = acDefault, tableName
Debug.Print "Table" & tableName & "deleted..."
DoCmd.SetWarnings True
End If
End Sub
Pretty much the code should work.

To delete the TempImport1 table if it exists just use the below function.
Function DeleteTables()
If Not IsNull(DLookup("Name", "MSysObjects", "Name='TempImport1' AND Type = 1")) Then
DoCmd.DeleteObject acTable, "TempImport1"
End If
End Function
Once the function has been created, create a macro, add the action run code then type in DeleteTables() in to the Function Name.
You then have a macro to run to delete the table if it exists.

Checking MSysObjects (used in other answers) misreported a table as existing if it was recently deleted. I found the following test more reliable.
Option Compare Database
Option Explicit
Public Sub DeleteIfExists()
Dim tableName As String
tableName = "Table3"
On Error Resume Next
Set td = db.TableDefs(tableName)
If Err.Number <> 0 Then
DoCmd.SetWarnings False
DoCmd.Close acTable, tableName, acSaveYes
DoCmd.DeleteObject acTable = acDefault, tableName
Debug.Print "Table" & tableName & "deleted..."
DoCmd.SetWarnings True
End If
End Sub

Here is the version I created to get rid of import error tables. The Err. Number must be 0 to actually remove table. TRACE is my internal flag.
Public Function RemoveImportErrorTables(Optional strTableBaseName As String = "rngExportDaily_ImportErrors") As Integer
'Purpose:
' Remove ImportError Tables
'In:
' Tables base Name
'Out:
' number of tables flushed
'History:
' Created 2021-12-06 16:10 Anton Sachs; Last modified 2021-12-06 16:15 Anton Sachs
'
Dim intResult As Integer
Dim strTableName As String
Dim dbCur As Database
Dim tdfTableDef As TableDef
Dim intTableIndex As Integer
On Error GoTo RemoveImportErrorTables_Err
Set dbCur = CurrentDb()
For intTableIndex = 0 To 100
If intTableIndex = 0 Then
strTableName = strTableBaseName
Else
strTableName = strTableBaseName & CStr(intTableIndex)
End If
On Error Resume Next
Set tdfTableDef = dbCur.TableDefs(strTableName)
If Err.Number = 0 Then
DoCmd.SetWarnings False
DoCmd.Close acTable, strTableName, acSaveYes
DoCmd.DeleteObject acTable = acDefault, strTableName
DoCmd.SetWarnings True
intResult = intResult + 1
Else
On Error GoTo RemoveImportErrorTables_Err
Exit For
End If
Next intTableIndex
RemoveImportErrorTables_Exit:
RemoveImportErrorTables = intResult
If Not tdfTableDef Is Nothing Then
Set tdfTableDef = Nothing
End If
If Not dbCur Is Nothing Then
Set dbCur = Nothing
End If
Exit Function
RemoveImportErrorTables_Err:
If TRACE = 0 Then TRACE = GetStandard("Trace")
If TRACE <> False Then
Debug.Print "Error " & Err.Number & " " & Err.Description & " in RemoveImportErrorTables"
Err.Clear
If TRACE = CTR±Stop Then
Stop
Resume Next
End If
Else
Err.Clear
Resume Next
End If
RemoveImportErrorTables_Fail:
intResult = False
GoTo RemoveImportErrorTables_Exit
End Function

Related

MS Access create forms for distinct field value in VBA

I have a VBA script and I need to create forms based on distinct LocationIDs in a table. So for every row with LocationID = 1 create a form with the name of that location represented in the title of the form, "formLocation1". Then for each LocationID = 2, create another form with the name of that one in the title, "formLocation2", etc. What is the best way to do this using DoCmd.OpenForm"" in the VBA script?
You can try something like this.
Loop through a recordset, and create a form for each LocationID using the CreateForm() method. You can then set the Form's .Caption property to "formLocation(LocationID)".
(Change T to the name of your table).
Public Sub CreateForms()
On Error GoTo ex
Dim rs As DAO.Recordset
Set rs = CurrentDb().OpenRecordset("SELECT DISTINCT LocationID FROM T ORDER BY LocationID;", dbOpenSnapshot)
With rs
If .EOF Then GoTo out
.MoveLast
.MoveFirst
End With
Dim frm As Access.Form, i As Integer
For i = 1 To rs.RecordCount
Set frm = CreateForm()
frm.Caption = "formLocation" & rs![LocationID]
DoCmd.Close acForm, frm.Name, acSaveYes
Set frm = Nothing
rs.MoveNext
Next i
out:
On Error Resume Next
rs.Close
Set rs = Nothing
On Error GoTo 0
Exit Sub
ex:
MsgBox Err.Description, vbCritical
Resume out
End Sub

Updating Excel Application.StatusBar within Access VBA

My current situation:
I am developing a culmination of VBA programs embedded in an excel file (named "Dashboard.xlsm" and an access file "Dashboard.accdb"). These two files talk to one another via VBA in order to help me do some heavy lifting on data that I need to analyze for my company. Because these programs are being distributed to several managers who panic when something doesn't complete within 3 seconds, I need a good way to indicate the progress of the SQL queries that are being run in Access through Excel (because Access is running invisibly in the background).
My current Excel code:
Sub generateFRMPComprehensive_ButtonClick(Optional sheetName As Variant)
Application.ScreenUpdating = False
Dim directoryPath As String
Dim cn As Object
Dim rs As Object
Dim strCon As String
Dim strSQL, strInput As String
Dim sArray As Variant
Dim appAccess As Access.Application
Dim directoryName
oldStatusBar = Application.DisplayStatusBar
Application.DisplayStatusBar = True
directoryName = Application.ActiveWorkbook.Path
directoryPath = CreateObject("WScript.Shell").SpecialFolders("Desktop") & "\Dashboard Exports"
Application.ScreenUpdating = False
If IsMissing(sheetName) Then
sheetName = Application.InputBox("Sheet Name?", "Sheet Selection")
If sheetName = "False" Then
Exit Sub
Else
End If
If FileFolderExists(directoryPath) = 0 Then
Application.StatusBar = "Creating Export Folder"
MkDir directoryPath
End If
End If
'-- Set the workbook path and name
reportWorkbookName = "Report for " & sheetName & ".xlsx"
reportWorkbookPath = directoryPath & "\" & reportWorkbookName
'-- end set
'-- Check for a report already existing
If FileExists(reportWorkbookPath) = True Then
Beep
alertBox = MsgBox(reportWorkbookName & " already exists in " & directoryPath & ". Do you want to replace it?", vbYesNo, "File Exists")
If alertBox = vbYes Then
Kill reportWorkbookPath
'-- Run the sub again with the new sheetName, exit on completion.
generateFRMPComprehensive_ButtonClick (sheetName)
Exit Sub
ElseIf alertBox = vbNo Then
Exit Sub
ElseIf alertBox = "False" Then
Exit Sub
End If
End If
'-- End check
'- Generate the report
'-- Create new access object
Set appAccess = New Access.Application
'-- End Create
'-- Open the acces project
Application.StatusBar = "Updating Access DB"
Call appAccess.OpenCurrentDatabase(directoryName & "\Dashboard.accdb")
appAccess.Visible = False
'-- End open
'-- Import New FRMP Data
Application.StatusBar = "Running SQL Queries"
appAccess.Application.Run "CleanFRMPDB", sheetName, directoryName & "\Dashboard.xlsm"
'-- End Import
Workbooks.Add
ActiveWorkbook.SaveAs "Report for " & sheetName
ActiveWorkbook.Close
appAccess.Application.Run "generateFRMPReport_Access", reportWorkbookPath
Workbooks.Open (reportWorkbookPath)
End Sub
My current Access Code:
Public Sub generateFRMPReport_Access(excelReportFileLocation As String)
Dim queriesList As Variant
queriesList = Array("selectAppsWithNoHolds", _
"selectAppsWithPartialHolds", _
"selectAppsCompleted", _
"selectAppsCompletedEPHIY", _
"selectAppsByDivision", _
"selectAppsByGroup", _
"selectAppsEPHIY", _
"selectAppsEPHIN", _
"selectAppsEPHIYN", _
"selectApps")
For i = 0 To 9
DoCmd.TransferSpreadsheet acExport, , queriesList(i), _
excelReportFileLocation, True
Next i
End Sub
My Request:
Is there a way that I can call the Application.DisplayStatusBar from within the 'for' loop within Access and pass the name of the query being run?
Alternatively, what other ways could I display this information?
Thank you!!
You have a few options for achieving this, but the two most obvious are to:
Execute the queries from Excel, and update the status bar from Excel
Execute the queries from Access, but pass the Excel Application reference to Access, so that Access can call back to the Excel status bar.
As your'e driving the activity from Excel, and you already have a reference to the Access Application, the first option is the most logical. The second approach is possible - you just need to pass the Excel object to Access, but then you'd be using Excel to automate Access to automate Excel.
You'll need to move the generateFRMPReport_Access procedure from the Access VBA into the Excel VBA, and modify your call to the procedure in generateFRMPComprehensive_ButtonClick
Sub generateFRMPComprehensive_ButtonClick(Optional sheetName As Variant)
'...
'appAccess.Application.Run "generateFRMPReport_Access", reportWorkbookPath
generateFRMPReport_Access reportWorkbookPath, appAccess
'...
End Sub
Public Sub generateFRMPReport_Access(excelReportFileLocation As String, appAccess As Access.Application)
Dim queriesList As Variant
Dim i As Long
queriesList = Array("selectAppsWithNoHolds", _
"selectAppsWithPartialHolds", _
"selectAppsCompleted", _
"selectAppsCompletedEPHIY", _
"selectAppsByDivision", _
"selectAppsByGroup", _
"selectAppsEPHIY", _
"selectAppsEPHIN", _
"selectAppsEPHIYN", _
"selectApps")
Application.DisplayStatusBar = True
For i = 0 To 9
Application.StatusBar = "Running query " & (i + 1) & " of 9"
appAccess.DoCmd.TransferSpreadsheet acExport, , queriesList(i), _
excelReportFileLocation, True
Next i
Application.StatusBar = False
Application.DisplayStatusBar = False
End Sub

VBA Excel check if a particular table exist using table name

I have several tables in an excel sheet. Each having unique table Name. I want to know if a table which has a name "Table123" exist or not in the current sheet.
Could some one help me on this?
Thanks
Jeevan
TableExists = False
On Error GoTo Skip
If ActiveSheet.ListObjects("Table123").Name = "Table123" Then TableExists = True
Skip:
On Error GoTo 0
This code will work and avoid loops and errors
Here is an alternative function:
Function TableExistsOnSheet(ws As Worksheet, sTableName As String) As Boolean
TableExistsOnSheet = ws.Evaluate("ISREF(" & sTableName & ")")
End Function
You can list shape collection and compare names like this
Sub callTableExists()
MsgBox tableExists("Table1", "Shapes")
End Sub
Function TableExists(tableName As String, sheetName As String) As Boolean
Dim targetSheet As Worksheet
Set targetSheet = Worksheets(sheetName)
Dim tbl As ListObject
With targetSheet
For Each tbl In .ListObjects
If tbl.Name = tableName Then TableExists = True
Next tbl
End With
End Function
Another option, using a bit lazy approach with error catching:
Public Sub TestMe()
If TableExists("Table1243", ActiveSheet) Then
MsgBox "Table Exists"
Else
MsgBox "Nope!"
End If
End Sub
Public Function TableExists(tableName As String, ws As Worksheet) As Boolean
On Error GoTo TableExists_Error
If ws.ListObjects(tableName).Name = vbNullString Then
End If
TableExists = True
On Error GoTo 0
Exit Function
TableExists_Error:
TableExists = False
End Function
Try this, use err to get data table status information
also, consider testing the data table on an inactive sheet.
Sub Test_TableNameExists()
TableNm = "Table00"
MsgOutput = TableNm & vbTab & TableNameExists(TableNm)
End Sub
Private Function TableNameExists(nname) As Boolean '#Table #Exist
'Returns TRUE if the data table name exists
'Can test table on inactive sheet
Dim x As Object
On Error Resume Next
'use Range(nname).Parent to get data table sheet name.
'So the function can test data table on inactive sheet.
Set x = Range(nname).Parent.ListObjects(nname)
If Err = 0 Then TableNameExists = True _
Else TableNameExists = False
End Function
Without the use of GoTo, which is a lot more powerfull than appropriate.
Set TableExists = False
On Error Resume Next
If ActiveSheet.ListObjects("Table123").Name = "Table123" Then Set TableExists = True
Be aware that this applies to a single line, thus requiring the line continuation symbol _ to keep larger statements readable.

MS Access logging users entrance and exit times across network

Hey guys I need some help. I was tasked with a business intelligence project where the initial step is logging users’ entrance and exit dates and times. So I have three modules:
modCaptureIns and modCaptureOuts; Also, I have modCaptureIns' function RecordIns() running on the AutoExec macro and modCaptureOuts running on when a hidden form named frmCaptureOuts is closed (upon user closing database).
Here is the problem: When a single user opens and closes the database entrance and exit times are logged in tblInsNOuts. As soon as you have more than one user, the second user is changing the SessId variable to a different number, and then the exit times are not recorded. Please give me a hand. Any help will be much appreciated.
CF
`Option Compare Database
Option Explicit
Public SessID As Integer
Public Function RecordIns()
On Error GoTo ErrorHandler
Dim db As Database
Dim rs As Recordset
DoCmd.SetWarnings False
SessID = DFirst("[MaxOfSessionID]", "[qryMaxOfSessionID]") + 1
Set db = CurrentDb
Set rs = db.OpenRecordset("tblInsNOuts")
rs.AddNew
rs.Fields("SessionID") = SessID
rs.Fields("WinID") = GetUser()
rs.Fields("EntryStamp") = Now()
rs.Update
DoCmd.SetWarnings True
rs.Close
Set rs = Nothing
db.Close
ExitSub:
Exit Function
ErrorHandler:
MsgBox "Error " & Err.Number & ": " & Err.Description
Resume ExitSub
End Function
Option Compare Database
Option Explicit
Public Function ExitStamp()
On Error GoTo ErrorHandler
Dim db As Database
Dim rs As Recordset
Dim fldEnumerator As Object
Dim fldColumns As Object
Set db = CurrentDb
Set rs = db.OpenRecordset("tblInsNOuts")
Set fldColumns = rs.Fields
DoCmd.SetWarnings False
While Not rs.EOF
For Each fldEnumerator In rs.Fields
If fldEnumerator.Name = "SessionID" Then
If fldEnumerator.Value = SessID Then
rs.Edit
rs.Fields("ExitStamp") = Now()
rs.Update
End If
End If
Next
rs.MoveNext
Wend
DoCmd.SetWarnings True
rs.Close
Set rs = Nothing
db.Close
ExitSub:
Exit Function
ErrorHandler:
MsgBox "Error " & Err.Number & ": " & Err.Description
Resume ExitSub
End Function
Option Compare Database
Private Sub Form_Close()
Call ExitStamp
End Sub
`
I went a step further with this. I was able to open another Windows image and therefore a second snapshot of the DB. When I opened both snapshots at the same time it logs the entry times for both, but when close the first one it drops the value of SessID. I realized the snapshot which remains open could no longer access the value of SessID because it's now null. Still need some help guys

vba error handling in loop

New to vba, trying an 'on error goto' but, I keep getting errors 'index out of range'.
I just want to make a combo box that is populated by the names of worksheets which contain a querytable.
For Each oSheet In ActiveWorkbook.Sheets
On Error GoTo NextSheet:
Set qry = oSheet.ListObjects(1).QueryTable
oCmbBox.AddItem oSheet.Name
NextSheet:
Next oSheet
I'm not sure whether the problem is related to nesting the On Error GoTo inside a loop, or how to avoid using the loop.
The problem is probably that you haven't resumed from the first error. You can't throw an error from within an error handler. You should add in a resume statement, something like the following, so VBA no longer thinks you are inside the error handler:
For Each oSheet In ActiveWorkbook.Sheets
On Error GoTo NextSheet:
Set qry = oSheet.ListObjects(1).QueryTable
oCmbBox.AddItem oSheet.Name
NextSheet:
Resume NextSheet2
NextSheet2:
Next oSheet
As a general way to handle error in a loop like your sample code, I would rather use:
on error resume next
for each...
'do something that might raise an error, then
if err.number <> 0 then
...
end if
next ....
How about:
For Each oSheet In ActiveWorkbook.Sheets
If oSheet.ListObjects.Count > 0 Then
oCmbBox.AddItem oSheet.Name
End If
Next oSheet
Actualy the Gabin Smith's answer needs to be changed a bit to work, because you can't resume with without an error.
Sub MyFunc()
...
For Each oSheet In ActiveWorkbook.Sheets
On Error GoTo errHandler:
Set qry = oSheet.ListObjects(1).QueryTable
oCmbBox.AddItem oSheet.name
...
NextSheet:
Next oSheet
...
Exit Sub
errHandler:
Resume NextSheet
End Sub
There is another way of controlling error handling that works well for loops. Create a string variable called here and use the variable to determine how a single error handler handles the error.
The code template is:
On error goto errhandler
Dim here as String
here = "in loop"
For i = 1 to 20
some code
Next i
afterloop:
here = "after loop"
more code
exitproc:
exit sub
errhandler:
If here = "in loop" Then
resume afterloop
elseif here = "after loop" Then
msgbox "An error has occurred" & err.desc
resume exitproc
End if
I do not want to craft special error handlers for every loop structure in my code so I have a way of finding problem loops using my standard error handler so that I can then write a special error handler for them.
If an error occurs in a loop, I normally want to know about what caused the error rather than just skip over it. To find out about these errors, I write error messages to a log file as many people do. However writing to a log file is dangerous if an error occurs in a loop as the error can be triggered for every time the loop iterates and in my case 80 000 iterations is not uncommon. I have therefore put some code into my error logging function that detects identical errors and skips writing them to the error log.
My standard error handler that is used on every procedure looks like this. It records the error type, procedure the error occurred in and any parameters the procedure received (FileType in this case).
procerr:
Call NewErrorLog(Err.number, Err.Description, "GetOutputFileType", FileType)
Resume exitproc
My error logging function which writes to a table (I am in ms-access) is as follows. It uses static variables to retain the previous values of error data and compare them to current versions. The first error is logged, then the second identical error pushes the application into debug mode if I am the user or if in other user mode, quits the application.
Public Function NewErrorLog(ErrCode As Variant, ErrDesc As Variant, Optional Source As Variant = "", Optional ErrData As Variant = Null) As Boolean
On Error GoTo errLogError
'Records errors from application code
Dim dbs As Database
Dim rst As Recordset
Dim ErrorLogID As Long
Dim StackInfo As String
Dim MustQuit As Boolean
Dim i As Long
Static ErrCodeOld As Long
Static SourceOld As String
Static ErrDataOld As String
'Detects errors that occur in loops and records only the first two.
If Nz(ErrCode, 0) = ErrCodeOld And Nz(Source, "") = SourceOld And Nz(ErrData, "") = ErrDataOld Then
NewErrorLog = True
MsgBox "Error has occured in a loop: " & Nz(ErrCode, 0) & Space(1) & Nz(ErrDesc, "") & ": " & Nz(Source, "") & "[" & Nz(ErrData, "") & "]", vbExclamation, Appname
If Not gDeveloping Then 'Allow debugging
Stop
Exit Function
Else
ErrDesc = "[loop]" & Nz(ErrDesc, "") 'Flag this error as coming from a loop
MsgBox "Error has been logged, now Quiting", vbInformation, Appname
MustQuit = True 'will Quit after error has been logged
End If
Else
'Save current values to static variables
ErrCodeOld = Nz(ErrCode, 0)
SourceOld = Nz(Source, "")
ErrDataOld = Nz(ErrData, "")
End If
'From FMS tools pushstack/popstack - tells me the names of the calling procedures
For i = 1 To UBound(mCallStack)
If Len(mCallStack(i)) > 0 Then StackInfo = StackInfo & "\" & mCallStack(i)
Next
'Open error table
Set dbs = CurrentDb()
Set rst = dbs.OpenRecordset("tbl_ErrLog", dbOpenTable)
'Write the error to the error table
With rst
.AddNew
!ErrSource = Source
!ErrTime = Now()
!ErrCode = ErrCode
!ErrDesc = ErrDesc
!ErrData = ErrData
!StackTrace = StackInfo
.Update
.BookMark = .LastModified
ErrorLogID = !ErrLogID
End With
rst.Close: Set rst = Nothing
dbs.Close: Set dbs = Nothing
DoCmd.Hourglass False
DoCmd.Echo True
DoEvents
If MustQuit = True Then DoCmd.Quit
exitLogError:
Exit Function
errLogError:
MsgBox "An error occured whilst logging the details of another error " & vbNewLine & _
"Send details to Developer: " & Err.number & ", " & Err.Description, vbCritical, "Please e-mail this message to developer"
Resume exitLogError
End Function
Note that an error logger has to be the most bullet proofed function in your application as the application cannot gracefully handle errors in the error logger. For this reason, I use NZ() to make sure that nulls cannot sneak in. Note that I also add [loop] to the second identical error so that I know to look in the loops in the error procedure first.
What about?
If oSheet.QueryTables.Count > 0 Then
oCmbBox.AddItem oSheet.Name
End If
Or
If oSheet.ListObjects.Count > 0 Then
'// Source type 3 = xlSrcQuery
If oSheet.ListObjects(1).SourceType = 3 Then
oCmbBox.AddItem oSheet.Name
End IF
End IF