Importing using VB not manual import - vba

I currently know how to import these files manually, but I am looking to execute a vb code through my import button to do it automatically. There is a field linked to the import button which requires you to enter a date and click import. Once import is clicked, I would like it to grab a file based on the date that is on the files name.
These files are text files and the file names are written in two types of format:
1st Format - P.RR1234.ABCDEF.D160112.T123456
2nd Format - G1234.ABCDEF.D160112.T123456
Here is my vb code for my current form (I currently have it mapped to my desktop but there are 100's of files located on a network/shared path that are in the format):
Option Compare Database
Private Sub cmdImport_Click()
On Error GoTo Click_Err
If Nz(txtReportDate, "") = "" Then
MsgBox "NOTICE! Please enter the Report Month you wish to Import."
Else
Dim rs As Recordset
Dim sql As String
'Loop through recordset of all Contracts and import files
sql = "SELECT DISTINCT FROM AAAAB_CE"
Set rs = CurrentDb.OpenRecordset(sql)
rs.MoveLast
rs.MoveFirst
If rs.RecordCount > 0 Then
Do While rs.EOF = False
ImportFile rs!DISTINCT
rs.MoveNext
Loop
End If
DoCmd.Hourglass True
DoCmd.SetWarnings False
DoCmd.Hourglass False
DoCmd.SetWarnings True
MsgBox "Finished Importing!"
DoCmd.OpenQuery "query_Files_Loaded_CE", acViewNormal, acReadOnly
click_Exit:
DoCmd.Hourglass False
DoCmd.SetWarnings True
Exit Sub
Click_Err:
DoCmd.Hourglass False
MsgBox "Error Detected: " & Err.Number & " - " & Err.Description, vbCritical, "Error"
Resume click_Exit
Exit Sub
End If
End Sub
Module:
Option Explicit
Option Compare Database
Public Function ImportFile(Contract As String)
DoCmd.TransferText acImportFixed, "123_Files", "AAAAB_CE", "C:\Users\123456\Desktop\TestingFolder\test.txt", False
End Function
I am trying to import these text files into one table. There are a lot of files, so manually would be insufficient.
Currently, if I put in a random date on the form text date and click import. It imports the file I select, but in a completely wrong format. Which is weird because the specification I saved was done in the correct format.
All in all I am trying to accomplish three things.
1) Import automatically by entering a date in a field on form (the date will be part of the file name) and clicking the import buton
2) Import the text file in the correct structure, fixed columns
3) If an incorrect date is entered, do not import anything.

I couldn't troubleshoot this code without knowing the date format used in the txt files and the files themselves, but this is how I would attack it:
use Dir to loop through the files at specified folder location searching for .txt files that contains the date,
then calling your ImportFile function (which is really a sub no?) by passing the filename.
Hopefully enough there for you to follow the logic..
Public Sub sampleSub()
Dim dirStr As String
Dim filePath As String
Dim fileDate As Date
filePath = "C:\Users\123456\Desktop\TestingFolder\"
On Error GoTo doNothing:
fileDate = DateValue(InputBox("NOTICE! Please enter the Report Month you wish to Import."))
dirStr = Dir(filePath & "\" & "*" * Format(fileDate, "FileDateFormat") & "*" & ".txt")
Do Until dirStr = ""
Call ImportFile(dirStr)
Loop
Exit Sub
doNothing:
Call MsgBox("Error Detected: " & Err.Number & " - " & Err.Description, vbCritical, "Error")
End Sub
Public Sub ImportFile(fileName As String)
Call DoCmd.TransferText(acImportFixed, "123_Files", "AAAAB_CE", fileName, False)
End Sub
Hope this helps,
TheSilkCode

Related

MS Access: Upload multiple files from one button

I am trying to upload multiple files at once into an access database via the use of a button. However only one file will upload at a time.
When the button is clicked it calls a sub procedure. My code is below:
Private Sub btnImport_Click()
'Calls the procdure that imports raw files
Call Module1.ImportRawFiles
End Sub
Public Sub ImportRawFiles()
Dim oFileDiag As Office.FileDialog
Dim path As String: path = ""
Dim oFSO As New FileSystemObject
Dim FileSelected As Variant
Set oFileDiag = Application.FileDialog(msoFileDialogFilePicker) ''Picks file to import
oFileDiag.AllowMultiSelect = True ''Allows multiple files to be selected
oFileDiag.Title = "Please select the reports to upload"
oFileDiag.Filters.Clear
oFileDiag.Filters.Add "Excel Spreadsheets", "*.xlsx, *.xls" ''Only allows xlsx and xls file types to upload
If oFileDiag.Show Then
For Each FileSelected In oFileDiag.SelectedItems
Form_Homepage.txtFileName = FileSelected
Next
End If
If Nz(Form_Homepage.txtFileName, "") = "" Then
MsgBox "No files selected please select a file"
Exit Sub
End If
If oFileDiag.SelectedItems.Count > 0 Then path = oFileDiag.SelectedItems(1)
If Len(path) > 0 Then
DoCmd.TransferSpreadsheet acImport, acSpreadsheetTypeExcel12, oFSO.GetFileName(Form_Homepage.txtFileName), path, 1
MsgBox "The " & oFSO.GetFileName(Form_Homepage.txtFileName) & " file has been uploaded"
Else
MsgBox "File not found"
End If
Does anyone know why only one file is uploading?
You are looping through all selected files to assign Form_Homepage.txtFileName but then not doing anything else in that same loop:
If oFileDiag.Show Then
For Each FileSelected In oFileDiag.SelectedItems
Form_Homepage.txtFileName = FileSelected
Next
End If
So by end of the loop, the last selected file is assigned, ignoring all the others, then your later logic statements only perform on that one file.
One solution would be to move your action logic up to the same loop. So move your IF statements into the assignment loop, that way they operate on each iterative assignment of your variable.

Access VB export certain query to designated filepath

So I have a query named "the query I wish to export", I want to be able to export the query to Excel when I click the button on my form.
I created this function in Module1 to call the dialog out and determine which file path I want to save my query result to.
Public Function ExportToExcel(strQuery As String)
On Error GoTo Err_Handler
Const MESSAGETEXT = "Overwrite existing file?"
Dim OpenDlg As New BrowseForFileClass
Dim strPath As String
OpenDlg.DialogTitle = "Enter or Select File"
strPath = OpenDlg.GetFileSpec
Set OpenDlg = Nothing
If strPath <> "" Then
If Dir(strPath) <> "" Then
If MsgBox(MESSAGETEXT, vbQuestion + vbYesNo, "Confirm") = vbNo Then
Exit Function
Else
Kill strPath
End If
End If
Else
Exit Function
End If
DoCmd.TransferSpreadsheet acExport, acSpreadsheetTypeExcel12, strQuery, strPath
Exit_Here:
Exit Sub
Err_Handler:
MsgBox Err.Description
Resume Exit_Here
End Function
After complete this function, I call this function and wish to export my query to the filepath that I wish to select.
Private Sub Export1_Click()
Call Module1.ExportToExcel "the query I wish to export"
End Sub
It just keeps giving me "Syntax Error". I don't really understand because I specifically call the function, passing the query name as its argument, any ideas?
Since you're evaluating the function using the Call keyword (which isn't strictly required), the arguments will need to be enclosed in parentheses, i.e.:
Call Module1.ExportToExcel("the query I wish to export")
For the file selection/specification, I would suggest using the FileDialog object, which will require a reference to the Microsoft Office ##.0 Object Library.
To provide an example of how this may be implemented, below is a quick function to demonstrate how you might go about prompting the user to specify/select an Excel file:
Function GetExcelFile(msg As String) As String
Dim dia As FileDialog
Set dia = Application.FileDialog(msoFileDialogFilePicker)
With dia
.AllowMultiSelect = False
.Title = msg
.Filters.Clear
.Filters.Add "Excel Files", "*.xls; *.xlsx"
If .show Then
GetExcelFile = .SelectedItems.Item(1)
End If
End With
End Function
Call the above with the desired dialog title, e.g.:
GetExcelFile "Enter or Select File"
The above will return an empty string if the user presses Cancel when prompted.

Use VBA Code to Update External Datasource Links

I am looking to use VBA to update links for an external input file. I am a developer and the path for the linked input file I use will not be the same as the end user will need once it is placed in a production folder.
Is there a way to update the linked file location using VBA? I already have code that allows the user to specify the input file location and that information is saved in the [InputFolder] of the [Defaults] table. Is there a way to use VBA to update the Linked Table using the InputFolder field info?
The stored InputFolder data looks like this:
C:\Users\CXB028\OneDrive - Comerica\Projects\HR\Input Data
The new folder info would have a network drive location path defined that I do not have access to but the user would.
Here is the code I use to define and store the Input Folder location:
Private Sub btnInputFldr_Click()
On Error GoTo Err_Proc
Const msoFileDialogFolderPicker As Long = 4
Dim objfiledialog As Object
Dim otable As DAO.TableDef
Dim strPathFile As String, strFile As String, strpath As String
Dim strTable As String
Dim fldr As Object
Set fldr = Application.FileDialog(msoFileDialogFolderPicker)
With fldr
.Title = "Choose Folder"
.Show
.InitialFileName = "" 'DFirst("InputFolder", "Defaults")
If .SelectedItems.Count = 0 Then
Exit Sub
Else
CurrentDb.Execute "UPDATE Defaults SET InputFolder='" & .SelectedItems(1) & "';"
End If
End With
Me.txtInputFldr.Requery
Exit Sub
Err_Proc:
MsgBox "Error " & Err.Number & ": " & Err.Description, vbCritical, "Process Error"
End Sub
The linked table (an external excel spreadsheet) needs to be re-linked after the database is moved to the production location using VBA code when the new Input Folder is redefined.
I found some very simple and short code the worked great!! Please see below.
On Error Resume Next
'Set new file path location if the TABLE.FIELDNAME location exists
Set tbl = db.TableDefs("ENTER THE LINKED TABLE NAME HERE")
filePath = DLookup("ENTER YOUR LOOKUP TABLE FIELD NAME HERE", "ENTER YOUR LOOKUP TABLE NAME HERE") & "\ENTER YOUR EXCEL SPREADSHEET NAME HERE.XLSX"
tbl.Connect = "Excel 12.0 Xml;HDR=YES;IMEX=2;ACCDB=YES;DATABASE=" & filePath
tbl.RefreshLink
On Error GoTo 0
Hope someone else finds this as useful as I did!

Importing text files - Vb/Access

What I am trying to do is map my button (import button on my form) to import text files (the text file would actually be on a network drive). These text files are fixed columns. I am confused on how to merge a form and module to work together. How does the button on the form, call out this module for execution? Also, if there is a more efficient way to import these fixed text files, I would appreciate it.
I currently have the following VBA code setup for my form (will be used to Import text files into my Access database):
Private Sub cmdImport_Click()
On Error GoTo Click_Err
reportDate = Format(txtReportDate, "YYMMDD")
reportGenDate = Format(textReportDate, "YYYYMMDD")
rDate = txtReportDate
If Nz(txtReportDate, "") = "" Then
MsgBox "NOTICE! Please enter the Report Month you wish to Import."
Exit Sub
End If
DoCmd.Hourglass True
DoCmd.SetWarnings False
ImportAll
DoCmd.Hourglass False
DoCmd.SetWarnings True
MsgBox "Finished Importing!"
DoCmd.OpenQuery "query_Files_Loaded_CE", acViewNormal, acReadOnly
click_Exit:
DoCmd.Hourglass False
DoCmd.SetWarnings True
Exit Sub
Click_Err:
DoCmd.Hourglass False
MsgBox "Error Detected: " & Err.Number & " - " & Err.Description, vbCritical, "Error"
Resume click_Exit
End Sub
For my module (please excuse the notes):
Option Compare Database
Public reportDate As String
Public reportGenDate As String
Public rDate As Date
Public Function Import2010()
'Used to import a date range
Dim funcDate As Date '
funcDate = #2/1/2016#
reportDate = Format(funcDate, "YYMM")
rDate = funcDate
'Basically Do While is a loop so what your doing here as long as the value of the date does not EQUAL 3/1/2016
'excute the nexxt line of code other wise exit this loop
Do While funcDate <> #3/1/2016#
DoCmd.SetWarnings False
'ImportAll
ImportFile "H3561"
'Msg Box reportDate
funcDate = DateAdd("m", 1, funcDate)
reportDate = Format(funcDate, "YYMM")
rDate = funcDate
Loop
DoCmd.SetWarnings True
End Function
Public Function ImportAll() ' Import button on FrmIMport
'A recordset is a selection of records from a table or query.
'Dim is short for the word Dimension and it allows you to declare variable names and their type.
'When you read data from the database in VBA, the result will be in a recordset (with the exception of scalar data).
Dim rs As Recordset
Dim sql As String
'This code loops through the recordset of all contracts and import files, as in it looks for
'Specific value based off a specific condition.
sql = "SELECT DISTINCT Contract FROM Contract_CE"
Set rs = CurrentDb.OpenRecordset(sql)
rs.MoveLast 'This method is used to move to the last record in a Recordset object. It also makes the last record the current record.
rs.MoveFirst 'This method is used to move to the first record in a Recordset object. It also makes the first record the current record.
If rs.RecordCount > 0 Then
Do While rs.EOF = False
ImportFile rs!contract
rs.MoveNext 'This method is used to move to the next record in a Recordset object. It also makes the "next" record the current record.
Loop
End If
End Function
Public Function ImportFile(contract As String)
Dim filepath As String
Dim tempPath As String
Dim zipFile As String
'Set paths
filepath = "\\XXXXX\XXXXX\XXXXX\XXXXXXX"
'tempPath =
tempPath = "\\XXXXXX\XXXXX\XXXXX\XX"
'Find the file
zipFile = GetFile(filepath)
'check if file exists
If zipFile = "" Then
'DoCmd.Hourglass False
'MsgBox contract & " " & reportDate & " File could not be located."
'DoCmd.Hourglass True
LogFail (contract)
Exit Function
End If
'Clearing out existing Contract/ReportDate data from Table
DeleteContract (contract)
'Delete all files in temp folder
DeleteAllFiles (tempPath)
'UnzipFile txt to temp folder
UnZip filepath & zipFile, tempPath
'Get txt file namee
txtFile = Replace(zipFile, ".zip", ".txt")
DoEvents
Sleep 10000 'wait for file to unzip
'The TransferText method is used to import/export text between the current Access database or Access project and a text file located
'externally to your database. You can also use this command to link to data in a text file. Additionally, can import from, export to, and link to a table in an HTML file.
'Importing txt file
'Depcreated - Alec Johnson - 5/12/2016 - Created new import spec
'DoCMD.TransferText acImportFixed, "ImportSpec_COMPRPT", tempPath & txtfile, False
DoCmd.TransferText acImportFixed, "COMPRPT_2016", "COMPRPT_CE", filepath & txtFile, False '<--does path go here?
'Update FileName
UpdateFileName (zipFile)
'Delete txt file from location
DeleteAllFiles (tempPath)
'Delete any Null records added to main table
DeleteNulls
'Log to table if successful
LogSuccess (contract)
End Function
Public Function DeleteAllFiles(path As String)
'Delete all files in this folder
On Error Resume Next
Kill path & "*.*"
End Function
Function UnZip(filename As String, destinationPath As String)
'FileSystemObject also called as FSO, provides an easy object based model to access computer’s file system.
'You simply have to create an instance of FileSystemObject in VBA and then you can generate files, read files, delete files,
'iterate though folders and do many other operations on your computer’s file system.
'Unzip file (s) to destination
Dim app As Object
Dim zipFile As Variant, unzipTo As Variant
zipFile = filename
unzipTo = destinationPath
Set FSO = CreateObject("Scripting.FileSystemObject")
If Not FSO.FolderExists(unzipTo) Then
FSO.CreateFolder (unzipTo)
End If
'If you want to extract only file you can use this:
'oApp.Namespace(FileNameFolder).CopyHere _
'oApp.Namespace(Fname).items.items("test.txt")
Set oApp = CreateObject("Shell.Application")
oApp.Namespace(unzipTo).CopyHere oApp.Namespace(zipFile).Items
Set FSO = Nothing
End Function
Public Function GetFile(filepath As String) As String
Dim fileNamePart As String
Dim fCheck
fileNamePart = "COMPRPT_" + reportDate
fCheck = ""
fFound = ""
Set oFolder = CreateObject("scripting.filesystemobject").GetFolder(filepath)
For Each aFile In oFolder.Files
Set fCheck = aFile
If InStr(fCheck.Name, fileNamePart) Then
Set fFound = aFile
End If
Next
If fFound = "" Then
GetFile = ""
Else
GetFile = fFound.Name
End If
End Function
Public Function DeleteContract(contract As String)
Dim sql As String
sql = "Delete * FROM COMPRPT WHERE ContractNumber = '" & contract & "' AND ReportGenerationDate = '" & reportGenDate & "'"
DoCmd.RunSQL sql
End Function
Public Function LogSuccess(contract As String)
Dim sql As String
sql = "INSERT INTO FilesLoaded (Contract, ReportDate, Loaded) VALUES ('" & contract & "', #" & rDate & "#, -1)"
DoCmd.RunSQL sql
End Function
Public Function DeleteNulls()
Dim sql As String
sql = "DELETE * FROM COMPRPT WHERE ContractNumber Is Null"
DoCmd.RunSQL sql
End Function
Public Function lksjdlaskjd()
ImportFile "H0351", #4/1/2009#
End Function
Here is an example of a text file:
If I understand it correctly, your problem lies here:
DoCmd.TransferText acImportFixed, "COMPRPT_2016", "COMPRPT_CE", filepath & txtFile, False '<--does path go here?
But you have unzipped to tempPath, so that should be
DoCmd.TransferText acImportFixed, "COMPRPT_2016", "COMPRPT_CE", tempPath & txtFile, False
Working with network files is generally slower than with local files, so I would make tempPath a local path.
Edit: Note that to make tempPath & txtFile work, tempPath must end with a \:
tempPath = "C:\XXXXXX\XXXXX\XXXXX\XX\"
Additional problems with your code:
1 - First and foremost, use Option Explicit, see this question for details.
You have multiple undeclared or misspelled variables, e.g. fFound, and oApp vs. app.
2 - This is an error just waiting to happen:
reportDate = Format(txtReportDate, "YYMMDD")
reportGenDate = Format(textReportDate, "YYYYMMDD")
Name the second textbox txtReportGenDate, not textReportDate.
3 - In ImportAll(), all this isn't needed, since you don't use the RecordCount:
rs.MoveLast
rs.MoveFirst
If rs.RecordCount > 0 Then
4 - This is wrong syntax:
DeleteContract (contract)
It works for a single argument, but will fail for subs with >1 parameters.
Use
DeleteContract contract
or
Call DeleteContract(contract)
or
retVal = DeleteContract(contract)
I am confused on how to merge a form and module to work together. How does the button on the form, call out this module for execution?
Objects and procedures can be considered public or private. For example: -
Private Sub Test
Msgbox "Hello World!"
End Sub
Is private, this means only its parent can call upon it. To elaborate on this, Lets create two modules Module1 and Module2 and place our private sub Test in Module1.
Also in Module1 we another private procedure: -
Private Sub Test2
Msgbox "Back at ya"
End Sub
Module1 is the parent of Test and Test2, as they have the same parent they can run each other: -
Private Sub Test
Msgbox "Hello World!"
Test2 'This will run the Test2 procedure
End Sub
Module2 can not run any of them because it has no view f them, its not involved.
Now if we change Test to be public (Public Sub Test), Module2 will be able to see it as it has been exposed.
In Module2 we have: -
Public Sub Test3
Module1.Test 'This will run as it is public
Module1.Test2 'This will fail as it is private
End Sub
There is also this way too call them from Module two: -
Public Sub Test3
Test 'This will run as it is public
Test2 'This will fail as it is private
End Sub
This is not explicit though and can cause error and confusion, you can have a procedure in Module2 that is also called Test, how would you know which test Test3 is running? To be safe you explicit write its location as Module1.Test.

MS Access: how to compact current database in VBA

Pretty simple question, I know.
If you want to compact/repair an external mdb file (not the one you are working in just now):
Application.compactRepair sourecFile, destinationFile
If you want to compact the database you are working with:
Application.SetOption "Auto compact", True
In this last case, your app will be compacted when closing the file.
My opinion: writting a few lines of code in an extra MDB "compacter" file that you can call when you want to compact/repair an mdb file is very usefull: in most situations the file that needs to be compacted cannot be opened normally anymore, so you need to call the method from outside the file.
Otherwise, the autocompact shall by default be set to true in each main module of an Access app.
In case of a disaster, create a new mdb file and import all objects from the buggy file. You will usually find a faulty object (form, module, etc) that you will not be able to import.
If you have the database with a front end and a back end. You can use the following code on the main form of your front end main navigation form:
Dim sDataFile As String, sDataFileTemp As String, sDataFileBackup As String
Dim s1 As Long, s2 As Long
sDataFile = "C:\MyDataFile.mdb"
sDataFileTemp = "C:\MyDataFileTemp.mdb"
sDataFileBackup = "C:\MyDataFile Backup " & Format(Now, "YYYY-MM-DD HHMMSS") & ".mdb"
DoCmd.Hourglass True
'get file size before compact
Open sDataFile For Binary As #1
s1 = LOF(1)
Close #1
'backup data file
FileCopy sDataFile, sDataFileBackup
'only proceed if data file exists
If Dir(sDataFileBackup, vbNormal) <> "" Then
'compact data file to temp file
On Error Resume Next
Kill sDataFileTemp
On Error GoTo 0
DBEngine.CompactDatabase sDataFile, sDataFileTemp
If Dir(sDataFileTemp, vbNormal) <> "" Then
'delete old data file data file
Kill sDataFile
'copy temp file to data file
FileCopy sDataFileTemp, sDataFile
'get file size after compact
Open sDataFile For Binary As #1
s2 = LOF(1)
Close #1
DoCmd.Hourglass False
MsgBox "Compact complete. " & vbCrLf & vbCrLf _
& "Size before: " & Round(s1 / 1024 / 1024, 2) & "MB" & vbCrLf _
& "Size after: " & Round(s2 / 1024 / 1024, 2) & "MB", vbInformation
Else
DoCmd.Hourglass False
MsgBox "ERROR: Unable to compact data file."
End If
Else
DoCmd.Hourglass False
MsgBox "ERROR: Unable to backup data file."
End If
DoCmd.Hourglass False
Try adding this module, pretty simple, just launches Access, opens the database, sets the "Compact on Close" option to "True", then quits.
Syntax to auto-compact:
acCompactRepair "C:\Folder\Database.accdb", True
To return to default*:
acCompactRepair "C:\Folder\Database.accdb", False
*not necessary, but if your back end database is >1GB this can be rather annoying when you go into it directly and it takes 2 minutes to quit!
EDIT: added option to recurse through all folders, I run this nightly to keep databases down to a minimum.
'accCompactRepair
'v2.02 2013-11-28 17:25
'===========================================================================
' HELP CONTACT
'===========================================================================
' Code is provided without warranty and can be stolen and amended as required.
' Tom Parish
' TJP#tomparish.me.uk
' http://baldywrittencod.blogspot.com/2013/10/vba-modules-access-compact-repair.html
' DGF Help Contact: see BPMHelpContact module
'=========================================================================
'includes code from
'http://www.ammara.com/access_image_faq/recursive_folder_search.html
'tweaked slightly for improved error handling
' v2.02 bugfix preventing Compact when bAutoCompact set to False
' bugfix with "OLE waiting for another application" msgbox
' added "MB" to start & end sizes of message box at end
' v2.01 added size reduction to message box
' v2.00 added recurse
' v1.00 original version
Option Explicit
Function accSweepForDatabases(ByVal strFolder As String, Optional ByVal bIncludeSubfolders As Boolean = True _
, Optional bAutoCompact As Boolean = False) As String
'v2.02 2013-11-28 17:25
'sweeps path for .accdb and .mdb files, compacts and repairs all that it finds
'NB: leaves AutoCompact on Close as False unless specified, then leaves as True
'syntax:
' accSweepForDatabases "path", [False], [True]
'code for ActiveX CommandButton on sheet module named "admin" with two named ranges "vPath" and "vRecurse":
' accSweepForDatabases admin.Range("vPath"), admin.Range("vRecurse") [, admin.Range("vLeaveAutoCompact")]
Application.DisplayAlerts = False
Dim colFiles As New Collection, vFile As Variant, i As Integer, j As Integer, sFails As String, t As Single
Dim SizeBefore As Long, SizeAfter As Long
t = Timer
RecursiveDir colFiles, strFolder, "*.accdb", True 'comment this out if you only have Access 2003 installed
RecursiveDir colFiles, strFolder, "*.mdb", True
For Each vFile In colFiles
'Debug.Print vFile
SizeBefore = SizeBefore + (FileLen(vFile) / 1048576)
On Error GoTo CompactFailed
If InStr(vFile, "Geographical Configuration.accdb") > 0 Then MsgBox "yes"
acCompactRepair vFile, bAutoCompact
i = i + 1 'counts successes
GoTo NextCompact
CompactFailed:
On Error GoTo 0
j = j + 1 'counts failures
sFails = sFails & vFile & vbLf 'records failure
NextCompact:
On Error GoTo 0
SizeAfter = SizeAfter + (FileLen(vFile) / 1048576)
Next vFile
Application.DisplayAlerts = True
'display message box, mark end of process
accSweepForDatabases = i & " databases compacted successfully, taking " & CInt(Timer - t) & " seconds, and reducing storage overheads by " & Int(SizeBefore - SizeAfter) & "MB" & vbLf & vbLf & "Size Before: " & Int(SizeBefore) & "MB" & vbLf & "Size After: " & Int(SizeAfter) & "MB"
If j > 0 Then accSweepForDatabases = accSweepForDatabases & vbLf & j & " failures:" & vbLf & vbLf & sFails
MsgBox accSweepForDatabases, vbInformation, "accSweepForDatabases"
End Function
Function acCompactRepair(ByVal pthfn As String, Optional doEnable As Boolean = True) As Boolean
'v2.02 2013-11-28 16:22
'if doEnable = True will compact and repair pthfn
'if doEnable = False will then disable auto compact on pthfn
On Error GoTo CompactFailed
Dim A As Object
Set A = CreateObject("Access.Application")
With A
.OpenCurrentDatabase pthfn
.SetOption "Auto compact", True
.CloseCurrentDatabase
If doEnable = False Then
.OpenCurrentDatabase pthfn
.SetOption "Auto compact", doEnable
End If
.Quit
End With
Set A = Nothing
acCompactRepair = True
Exit Function
CompactFailed:
End Function
'source: http://www.ammara.com/access_image_faq/recursive_folder_search.html
'tweaked slightly for error handling
Private Function RecursiveDir(colFiles As Collection, _
strFolder As String, _
strFileSpec As String, _
bIncludeSubfolders As Boolean)
Dim strTemp As String
Dim colFolders As New Collection
Dim vFolderName As Variant
'Add files in strFolder matching strFileSpec to colFiles
strFolder = TrailingSlash(strFolder)
On Error Resume Next
strTemp = ""
strTemp = Dir(strFolder & strFileSpec)
On Error GoTo 0
Do While strTemp <> vbNullString
colFiles.Add strFolder & strTemp
strTemp = Dir
Loop
If bIncludeSubfolders Then
'Fill colFolders with list of subdirectories of strFolder
On Error Resume Next
strTemp = ""
strTemp = Dir(strFolder, vbDirectory)
On Error GoTo 0
Do While strTemp <> vbNullString
If (strTemp <> ".") And (strTemp <> "..") Then
If (GetAttr(strFolder & strTemp) And vbDirectory) <> 0 Then
colFolders.Add strTemp
End If
End If
strTemp = Dir
Loop
'Call RecursiveDir for each subfolder in colFolders
For Each vFolderName In colFolders
Call RecursiveDir(colFiles, strFolder & vFolderName, strFileSpec, True)
Next vFolderName
End If
End Function
Private Function TrailingSlash(strFolder As String) As String
If Len(strFolder) > 0 Then
If Right(strFolder, 1) = "\" Then
TrailingSlash = strFolder
Else
TrailingSlash = strFolder & "\"
End If
End If
End Function
For Access 2013, you could just do
Sendkeys "%fic"
This is the same as typing ALT, F, I, C on your keyboard.
It's probably a different sequence of letters for different versions, but the "%" symbol means "ALT", so keep that in the code. you may just need to change the letters, depending on what letters appear when you press ALT
Letters that appear when pressing ALT in Access 2013
In response to the excellent post by jdawgx:
Please be aware of a flaw in the code for CompactDB() above.
If the database's "AppTitle" property is defined (as happens when an "Application title" is defined in the database properties), this invalidates the "default window title" logic shown, which can cause the script to fail, or "behave unpredictably". So, adding code to check for an AppTitle property - or using API calls to read the Window title text from the Application.hWndAccessApp window could both be much more reliable.
Additionally, in Access 2019, we have observed that:
SendKeys "multi-key-string-here"
... may also not work reliably, needing to be replaced with:
SendKey (single-character)
'put a DoEvents or Sleep 150 here
SendKey (single-character)
'put a DoEvents or Sleep 150 here
SendKey (single-character)
'put a DoEvents or Sleep 150 here
SendKey (single-character)
...to get proper responses from the Access UI.
ALSO for Access 2019:
Sendkeys "%yc" ( <-- works for Access 2016)
is no longer correct.
it is now:
Sendkeys "%y1c"
...and if that little change wasn't enough - try to determine (in code) how to tell the difference between Access 2016 and 2019 - Good Luck!! because
Application.Version alone won't help, and even combining Application.Version and Application.Build is not a guarantee (unless you are in a controlled-release enterprise environment, and then it may work as the possible version/build #s in circulation should be more limited).
Yes it is simple to do.
Sub CompactRepair()
Dim control As Office.CommandBarControl
Set control = CommandBars.FindControl( Id:=2071 )
control.accDoDefaultAction
End Sub
Basically it just finds the "Compact and repair" menuitem and clicks it, programatically.
I did this many years back on 2003 or possibly 97, yikes!
If I recall you need to use one of the subcommands above tied to a timer. You cannot operate on the db with any connections or forms open.
So you do something about closing all forms, and kick off the timer as the last running method. (which will in turn call the compact operation once everything closes)
If you haven't figured this out I could dig through my archives and pull it up.
When the user exits the FE attempt to rename the backend MDB preferably with todays date in the name in yyyy-mm-dd format. Ensure you close all bound forms, including hidden forms, and reports before doing this. If you get an error message, oops, its busy so don't bother. If it is successful then compact it back.
See my Backup, do you trust the users or sysadmins? tips page for more info.
DBEngine.CompactDatabase source, dest
Application.SetOption "Auto compact", False '(mentioned above)
Use this with a button caption: "DB Not Compact On Close"
Write code to toggle the caption with "DB Compact On Close"
along with Application.SetOption "Auto compact", True
AutoCompact can be set by means of the button or by code, ex: after importing large temp tables.
The start up form can have code that turns off Auto Compact, so that it doesn't run every time.
This way, you are not trying to fight Access.
If you don't wish to use compact on close (eg, because the front-end mdb is a robot program that runs continually), and you don't want to create a separate mdb just for compacting, consider using a cmd file.
I let my robot.mdb check its own size:
FileLen(CurrentDb.Name))
If its size exceeds 1 GB, it creates a cmd file like this ...
Dim f As Integer
Dim Folder As String
Dim Access As String
'select Access in the correct PF directory (my robot.mdb runs in 32-bit MSAccess, on 32-bit and 64-bit machines)
If Dir("C:\Program Files (x86)\Microsoft Office\Office\MSACCESS.EXE") > "" Then
Access = """C:\Program Files (x86)\Microsoft Office\Office\MSACCESS.EXE"""
Else
Access = """C:\Program Files\Microsoft Office\Office\MSACCESS.EXE"""
End If
Folder = ExtractFileDir(CurrentDb.Name)
f = FreeFile
Open Folder & "comrep.cmd" For Output As f
'wait until robot.mdb closes (ldb file is gone), then compact robot.mdb
Print #f, ":checkldb1"
Print #f, "if exist " & Folder & "robot.ldb goto checkldb1"
Print #f, Access & " " & Folder & "robot.mdb /compact"
'wait until the robot mdb closes, then start it
Print #f, ":checkldb2"
Print #f, "if exist " & Folder & "robot.ldb goto checkldb2"
Print #f, Access & " " & Folder & "robot.mdb"
Close f
... launches the cmd file ...
Shell ExtractFileDir(CurrentDb.Name) & "comrep.cmd"
... and shuts down ...
DoCmd.Quit
Next, the cmd file compacts and restarts robot.mdb.
Try this. It works on the same database in which the code resides. Just call the CompactDB() function shown below. Make sure that after you add the function, you click the Save button in the VBA Editor window prior to running for the first time. I only tested it in Access 2010. Ba-da-bing, ba-da-boom.
Public Function CompactDB()
Dim strWindowTitle As String
On Error GoTo err_Handler
strWindowTitle = Application.Name & " - " & Left(Application.CurrentProject.Name, Len(Application.CurrentProject.Name) - 4)
strTempDir = Environ("Temp")
strScriptPath = strTempDir & "\compact.vbs"
strCmd = "wscript " & """" & strScriptPath & """"
Open strScriptPath For Output As #1
Print #1, "Set WshShell = WScript.CreateObject(""WScript.Shell"")"
Print #1, "WScript.Sleep 1000"
Print #1, "WshShell.AppActivate " & """" & strWindowTitle & """"
Print #1, "WScript.Sleep 500"
Print #1, "WshShell.SendKeys ""%yc"""
Close #1
Shell strCmd, vbHide
Exit Function
err_Handler:
MsgBox "Error " & Err.Number & ": " & Err.Description
Close #1
End Function
Please Note the following - all of you who favor doing a "Compact on Close" solution for MS-Access.
I used to prefer that option too, until one day, when I received the WORST error message possible from the DBEngine during a Compress & Repair operation:
"Table MSysObjects is corrupt - Table Truncated."
Now, you have probably never realized that THAT error is even a possibility.
Well, it is. And if you ever see it, your ENTIRE DATABASE, and EVERYTHING IN IT is now simply GONE. poof!
What is funny about that is that Access will let you actually reopen the "fixed" database, only, the Access window and menu items are all now utterly useless (except to close the DB and exit access again) because ALL the tables (including the other MSYS* tables, forms, queries, reports, code modules, & macros) are simply gone - and with the disk space previously allocated to them released to the tender mercies of the Windows OS - unless you have additional protection than the bog-standard recycle bin, which won't help you either.
So, if you REALLY want to accept the risk of Compact on Close completely clobbering your database - with NO POSSIBILITY of recovering it, then please...do carry on.
If, OTOH, like me you find that risk an unacceptable one, well, don't enable C&R-on-Close - ever again.
Check out this solution VBA Compact Current Database.
Basically it says this should work
Public Sub CompactDB()
CommandBars("Menu Bar").Controls("Tools").Controls ("Database utilities"). _
Controls("Compact and repair database...").accDoDefaultAction
End Sub
There's also Michael Kaplan's SOON ("Shut One, Open New") add-in. You'd have to chain it, but it's one way to do this.
I can't say I've had much reason to ever want to do this programatically, since I'm programming for end users, and they are never using anything but the front end in the Access user interface, and there's no reason to regularly compact a properly-designed front end.