The title says it about all. I'm trying to write a VBA script that would allow me to run inside Access and it would export all database tables as separate sheets into and Excel file with the same name as the database:
Sub exportTablesToXLS()
Dim td As DAO.TableDef, db As DAO.Database
Dim out_file As String
out_file = CurrentProject.Path & "\" & db.DatabaseName & ".xls"
Set db = CurrentDb()
For Each td In db.TableDefs
If Left(td.Name, 4) = "MSys" Then
'We do not need MSys tables in excel file
Else
DoCmd.TransferSpreadsheet acExport, acSpreadsheetTypeExcel9, _
td.Name, out_file, True, Replace(td.Name, "dbo_", "") 'We do not need dbo prefix in sheetnames
End If
Next
End Sub
Problems I'm having I would like your help on:
see line out_file -> db.DatabaseName return an error. How can I correctly get the database name of the current Access database?
I want to output a logfile (simple textfile) as well. How can I read, for each database table, the number of rows that have been exported and report eventual errors that occured?
Any help to improve this script is greatly appreciated :-)
Your db variable doesn't refer to the current database (yet - it is set in the following line), and the property is Name (I haven't encountered DatabaseName):
Sub Test()
Dim db As DAO.Database
Set db = CurrentDb
Dim sLast As String
MsgBox db.Name
'F:\Documents and Settings\student\My Documents\Staff Database.accdb
sLast = InStrRev(db.Name, "\")
MsgBox Right(db.Name, Len(db.Name) - sLast)
'Staff Database.accdb
End Sub
Name gives the full path and filename, the second MsgBox reduces this to just the filename.
(There may be another way to get the filename without having to parse Name..)
To get the number of rows exported you could open a Recordset for the table(s), reading the RecordCount property. A text file could be created using the FileSystemObject object. Sample code:
Set fso = CreateObject("Scripting.FileSystemObject")
Set oFile = FSO.CreateTextFile(strPath)
oFile.WriteLine "test"
oFile.Close
Or even just simple file (VBA) I/O:
Open pathname For mode [Access access] [lock] As [#]filenumber [Len=reclength]
(the FileSystemObject is easier to work with)
To report errors you'll need to create your own error-handling routines, and an error-logging procedure. This has been done before and a little searching will uncover some code. Essentially, each (important) event has error-handling code that calls a logging procedure. This procedure creates a record in a table to store all relevant information: date, time, which form was open, etc., etc.
A quick Google uncovered this page about logging Access errors.
Related
We will put 100s of Excel worksheets out in the field this year. The code periodically needs to be updated when bugs are found. For last year's effort, I was able to dynamically have workbooks pull updates for .bas files. This year I want to dynamically have workbooks pull updates for the code embedded in the worksheets too.
EXPORT CODE
The export code is pretty simple, but there are artifacts in the .txt files
Sub SaveSoftwareFile(path$, name$, ext$)
ThisWorkbook.VBProject.VBComponents(name).Export path & name & ext
Example Call: SaveSoftwareFile path, "ThisWorkbook", ".txt"
The problem is that the export has a lot of header information that I don't care about (in red). I just want the part in blue. Is there switch that allows me not to save it, or do I have to manually go into the export and remove it myself?
IMPORT CODE
The import code is pretty straight forward too, but it causes the error "Can't enter break mode at this time", and I'm struggling to figure out the right path forward. If I manually try and delete this code, Excel is also unhappy. So maybe my approach is altogether incorrect. Here's the code:
Sub UpgradeSoftwareFile(path$, name$, ext$)
Dim ErrorCode%, dest As Object
On Error GoTo errhandler
Select Case ThisWorkbook.VBProject.VBComponents(name).Type
Case 1, 3 'BAS, FRM
<Not relevant for this discussion>
Case 100 'Worksheets
Set dest = ThisWorkbook.VBProject.VBComponents(name).codemodule
dest.DeleteLines 1, dest.CountOfLines 'Erase existing | Generates breakpoint error
dest.AddFromFile path & name & ext '| Also generates breakpoint error
End Select
Example Call: UpgradeSoftwareFile path, "ThisWorkbook", ".txt"
Thanks in advance for your help
Please, try the next way of exporting and you will not have the problem any more:
Sub SaveSoftwareFile(path$, sheetCodeModuleName$, FileName$)
Dim WsModuleCode As String, sCM As VBIDE.CodeModule, strPath As String, FileNum As Long
Set sCM = ThisWorkbook.VBProject.VBComponents(sheetCodeModuleName).CodeModule
WsModuleCode = sCM.Lines(1, sCM.CountOfLines)
'Debug.Print WsModuleCode
strPath = ThisWorkbook.path & "\" & FileName
FileNum = FreeFile
Open strPath For Output As #FileNum
Print #FileNum, WsModuleCode
Close #FileNum
End Sub
You can use the above Sub as following:
Sub testSaveSheetCodeModule()
Dim strPath As String, strFileName As String, strCodeModuleName As String
strPath = ThisWorkbook.path
strFileName = "SheetCode_x.txt"
strCodeModuleName = Worksheets("Test2").codename 'use here your sheet name
SaveSoftwareFile strPath, strCodeModuleName, strFileName
End Sub
Now, the created text file contains only the code itself, without the attributes saved by exporting the code...
Import part:
"Can't enter break mode at this time" does not mean that it is an error in the code. There are some operations (allowed only if a reference to Microsoft Visual Basic for Applications Extensibility ... exists) in code module manipulation, which cannot simple be run step by step. VBA needs to keep references to its VBComponents and it looks, it is not possible when changes in this area and in this way are made.
The import code is simple and it must run without problems. You must simple run the code and test its output...
So after searching through numerous other pages im still having difficulty and admittedly its due to self teaching myself VBA, so forgive me :(
I have created a simple Phone call counter Access form with a table. My goal is to press a button and it exports the table to a specified directory to an existing XLSM (the data exported would open a new worksheet with the current date as the worksheet name and then save it.
database location is here:
L:\Reports\TestCalltoolmetric.accdb
existing Excel file is here:
L:\Reports\Callcounterreports\MonthlyCallCounter.xlsm
What i have tried so far...
1.
Dim strTable As String
Dim strWorksheetPath As String
strWorksheetPath = "L:\Reports\Callcounterreports"
strWorksheetPath = strWorksheetPath & Format(Date, "ddmmmyy") & "Callreport.xlsx"
This exports my database to a new file with the name of the file as the date+callreport. It works but i dont know if i can use this command to accomplish my goal above (filename isnt really important but at the end of the month i need to provide call metrics so i need every days export all in one workbook so i can create totals).
2.
I tried using this:
DoCmd.TransferSpreadsheet acExport, acSpreadsheetTypeExcel12Xml, "Table1", "L:\Reports\Callcounterreports\MonthlyCallCounter.xlsm"
But it errors out saying that Access database engine could not find the object 'Table1'
3.
I also found through some furious googling a similar request but it was based off exporting a query...in an less than educated mannor i tried adapting it but never really was able to get it to work..
Dim appXL As Object
Dim wb As Object
Dim wks As Object
Dim xlf As String
Dim rs As DAO.Recordset
xlf = "L:\Reports\Callcounterreports\MonthlyCallCounter.xlsm" 'Full path to Excel file
Set rs = CurrentDb.OpenRecordset("Query1") 'Replace Query1 with real query name
Set appXL = CreateObject("Excel.Application")
Set wb = appXL.Workbooks.Open(xlf)
Set wks = wb.Sheets & Format (Date, "ddmmmyy") ' Sheet name
wb.Save
wb.Close
appXL.Quit
Set wb = Nothing
rs.Close
Set rs = Nothing
I know i am by far not the first person to probably ask about how to do this, but any help would be fantastic!
SO....after futher digging i figured out what i was doing wrong with at least using
DoCmd.TransferSpreadsheet acExport, acSpreadsheetTypeExcel12Xml, "Table1", "L:\Reports\Callcounterreports\MonthlyCallCounter.xlsm"
I first had to rename the table from "Table1" to another name, which i chose "DataTable".
Second i made a mistake on the filepath portion of the command, the location was a networked location which showed up a drive letter. after putting the full and correct file path the form worked like a charm.
https://msdn.microsoft.com/en-us/vba/access-vba/articles/docmd-transferspreadsheet-method-access
i used this website to verify my commands, which now i feel silly that i hadnt found it before.
Big thank you to #dbmitch i think frustration got the better of me instead of really reading the error info..
How can I create a Table link within a MS Access *.accdb file, linking to another table in an MS Access *.accdb file, but from VBA code within an MS Excel *.xlsx file.
Database1 in file1
Database2 in file2
Excel VBA Code in file3
Execute code in file3 to link a table of file2 to file1, so that it appears as linked table within file1.
Details:
I have a complex script creating a large table and join mappings based on a MS Excel Design, since it is more user friendly to design the things in an MS Excel Table. Everything works great so far, but I need the last step, LINK the original table.
Within MS Access I would do
DoCmd.TransferDatabase TransferType:=acLink, _
DatabaseType:="Microsoft Access", _
DatabaseName:=SRC_FILE_PATH, _
ObjectType:=acTable, _
Source:=SRC_TABLE_NAME, _
Destination:=DESTINATION_TABLE_NAME
But since I am executing VBA Code within Excel, only using the connection to the database via a DAO.Database object, this command doesn't work.
Using the DAO reference library in Excel
Dim wrkDAOWorkspace As DAO.Workspace
Dim dbsDAODatabase As DAO.Database
Dim tdfNewLinkedTable As DAO.TableDef
Set wrkDAOWorkspace = CreateWorkspace("WorkspaceForLinkCreation", "Admin", "", dbUseJet)
Set dbsDAODatabase = wrkDAOWorkspace.OpenDatabase("c:\file1.accdb")
Set tdfNewLinkedTable = New DAO.TableDef
With tdfNewLinkedTable
.Name = "TestTableLinked"
.Connect = ";DATABASE=c:\file2.accdb"
.SourceTableName = "TestTableLinkTo"
End With
dbsDAODatabase.TableDefs.Append tdfNewLinkedTable
Something like this should do it for you. Modify to suit your needs...
Private Sub Command1_Click()
DoCmd.SetWarnings False
Dim InputFile As String
Dim InputPath As String
InputPath = "C:\your_path_here\"
InputFile = Dir(InputPath & "*.xls")
Do While InputFile <> ""
DoCmd.TransferSpreadsheet acImport, , InputFile, InputPath & InputFile, True '< The true is for column headers
InputFile = Dir
Loop
End Sub
I have about 50 or so Excel workbooks that I need to pull data from. I need to take data from specific cells, specific worksheets and compile into one dataset (preferably into another excel workbook).
I am looking for some VBA so that I can compile the results into the workbook I am using to run the code.
So, one of the xls or xlsx files I need to pull the data from, worksheet("DataSource"), I need to evaluate cell(D4), and if its not null, then pull data from cell(F4), and put into a new row into the compiled data set. Looping through all the Excel files in that folder as mentioned above.
And if possible, I would like the first data field in the first column the name of the file the data is being pulled from in the resulting dataset.
Can someone help me with this? I am looking for VBA because I am more familiar with that, but also interested in VBScript (as I am trying to get into that and learn the differences).
First start with this google query and click the first link that comes up, which takes you to an article showing how to iterate through a group of Excel files in a folder.
Sub RunCodeOnAllXLSFiles()
Dim lCount As Long
Dim wbResults As Workbook
Dim wbCodeBook As Workbook
Application.ScreenUpdating = False
Application.DisplayAlerts = False
Application.EnableEvents = False
On Error Resume Next
Set wbCodeBook = ThisWorkbook
With Application.FileSearch
.NewSearch
'Change path to suit
.LookIn = "C:\MyDocuments\TestResults"
.FileType = msoFileTypeExcelWorkbooks
'Optional filter with wildcard
'.Filename = "Book*.xls"
If .Execute > 0 Then 'Workbooks in folder
For lCount = 1 To .FoundFiles.Count 'Loop through all
'Open Workbook x and Set a Workbook variable to it
Set wbResults = Workbooks.Open(Filename:=.FoundFiles(lCount), UpdateLinks:=0)
'DO YOUR CODE HERE
wbResults.Close SaveChanges:=False
Next lCount
End If
End With
On Error GoTo 0
Application.ScreenUpdating = True
Application.DisplayAlerts = True
Application.EnableEvents = True
End Sub
To get the name of the workbook, you'll want to adapt the code at "DO YOUR CODE HERE" to include wbResults.Name. If it's the filename you want, use wbResults.FullName, which returns the name of the workbook including its path on disk as a string.
A search for a VBScript variation on the same thing yields a number of results that are useful, including this script:
strPath = "C:\PATH_TO_YOUR_FOLDER"
Set objExcel = CreateObject("Excel.Application")
objExcel.Visible = True
objExcel.DisplayAlerts = False
Set objFso = CreateObject("Scripting.FileSystemObject")
Set objFolder = objFso.GetFolder (strPath)
For Each objFile In objFolder.Files
If objFso.GetExtensionName (objFile.Path) = "xls" Then
Set objWorkbook = objExcel.Workbooks.Open(objFile.Path)
' Include your code to work with the Excel object here
objWorkbook.Close True 'Save changes
End If
Next
objExcel.Quit
I would do it in VBScript or even, VB.NET or Powershell if you feel so inclined.
Using VB.NET, you can access Excel spreadsheets as if they were databases, via the OLEDB provider. The code to select a range of values might look like this :
Try
Dim MyConnection As System.Data.OleDb.OleDbConnection
Dim DtSet As System.Data.DataSet
Dim MyCommand As System.Data.OleDb.OleDbDataAdapter
MyConnection = New System.Data.OleDb.OleDbConnection _
("provider=Microsoft.Jet.OLEDB.4.0;" _
" Data Source='testfile.xls'; " _
"Extended Properties=Excel 8.0;")
MyCommand = New System.Data.OleDb.OleDbDataAdapter _
("select * from [Sheet1$]", MyConnection)
MyCommand.TableMappings.Add("Table", "TestTable")
DtSet = New System.Data.DataSet
MyCommand.Fill(DtSet)
MyConnection.Close()
Catch ex As Exception
MsgBox(ex.ToString)
End Try
Once you get the data you can elaborate on it, then insert the result into another Excel spreadsheet, using the same API.
Getting the list of files is easy in .NET with a call to System.IO.Directory.GetFiles(); just specify the "*.xls" wildcard. Once you have the list, just use a for loop to iterate through it, opening each file in turn, then doing the query on that file, and so on.
If you use VBScript, then the preferred way to get the list of Excel files is to use the Scripting.FileSystemObject, specifically the GetFolder method. It works basically the same way but the syntax is slightly different.
If it's VBScript or VB.NET it will probably run outside of Excel itself. You'd run it by double-clicking or from a batch file or something like that. The advantage to using VB.NET is you could put up a graphical form for interaction - it could show a progress bar, tracking how many files you've gone through, status updates, that kind of thing.
Whenever you are accessing that many Excel files in succession, you can generally get better performance using ADODB rather than Excel's automation object.
I agree with using that accessing the Excel object is not the quickest and if the workbooks and sheets that you're trying to retrieve data from are all consistent (i.e have the same column names, etc... or at least the column names you're looking for) it would be better to use ODBC. This does have some issues and if you can't get around them or need to actually do something more complex based on the contents then there may be no way around it. If that's the case then I would suggest creating one Excel object and then opening and closing the files as needed to try to increase the efficiency.
It could be done with the following code
Sub LoopThroughFiles()
Dim StrFile As String
StrFile = Dir("V:\XX\XXX\*.xlsx")
Do While Len(StrFile) > 0
Debug.Print StrFile
Set wbResults = Workbooks.Open("V:\XX\XXX\" & StrFile)
'DO YOUR CODE HERE
wbResults.Close SaveChanges:=True
StrFile = Dir
Loop
End Sub
Hey all, have been working on designing a new database for work. They have been using Excel for their daily reports and all the data is stored in there, so I decided to have the back-end of the database in Access and the front-end in Excel, so any analytical work can be easily performed once all the data has been imported into Excel.
Now I'm fairly new to VBA, slowly getting used to using it, have written some code to transfer one of the calculated tables from Access to Excel:
Option Explicit
Public Const DataLocation As String = "C:\Documents and Settings\Alice\Desktop\Database\TestDatabase21.accdb"
Sub Market_Update()
Call ImportFromAccessTable(DataLocation, "Final_Table", Worksheets(2).Range("A5"))
End Sub
Sub ImportFromAccessTable(DBFullName As String, TableName As String, TargetRange As Range)
Dim cn As ADODB.Connection, rs As ADODB.Recordset, intColIndex As Integer
Set TargetRange = TargetRange.Cells(1, 1)
' open the database
Set cn = New ADODB.Connection
cn.Open "Provider=Microsoft.ACE.OLEDB.12.0; Data Source=" & DBFullName & ";"
Set rs = New ADODB.Recordset
With rs
' open the recordset
' .Open TableName, cn, adOpenStatic, adLockOptimistic, adCmdTable
' all records
.Open "SELECT * FROM Final_Table", cn, , , adCmdText
' filter records
For intColIndex = 0 To rs.Fields.count - 1 ' the field names
TargetRange.Offset(0, intColIndex).Value = rs.Fields(intColIndex).Name
Next
TargetRange.Offset(1, 0).CopyFromRecordset rs ' the recordset data
End With
rs.Close
Set rs = Nothing
cn.Close
Set cn = Nothing
End Sub
Sub Company_Information()
Dim companyName As String
On Error GoTo gotoError
companyName = Application.InputBox(Prompt:="Enter Company Name", _
Title:="Company Name", Type:=2)
Exit Sub 'Don't execute errorhandler at end of routine
gotoError:
MsgBox "An error has occurred"
End Sub
The above code works fine and pulls up the desired calculated table and places it in the right cells in Excel.
I've got two problems that I'm having trouble with; firstly I have some cell-formatting already done for the cells where the data is going to be pasted into in Excel; I want it to apply the formatting to the values as soon as they are pasted in Excel.
Secondly; I have an add-on for Excel which updates some daily Stock Market values; these values need to be transferred into Access at the end of each working day, to keep the database maintained, I tried some code but have been having some problems with it running.
The code for this part can be seen following:
Sub UPDATE()
Dim cnt As ADODB.Connection
Dim stSQL As String, stCon As String, DataLocation As String
Dim stSQL2 As String
'database path - currently same as this workbook
DataLocation = ThisWorkbook.Path & DataLocation
stCon = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=" & DataLocation & ";"
'SQL code for GL Insert to Access
stSQL = "INSERT INTO Historical_Stock_Data SELECT * FROM [Portfolio] IN '" _
& ThisWorkbook.FullName & "' 'Excel 8.0;'"
'set connection variable
Set cnt = New ADODB.Connection
'open connection to Access db and run the SQL
With cnt
.Open stCon
.CursorLocation = adUseServer
.Execute (stSQL)
End With
'close connection
cnt.Close
'release object from memory
Set cnt = Nothing
End Sub
I get the following error with this.
Run-time Error '-2147467259 (80004005)'
The Microsoft Jet database engine cannot open the file 'Cocuments and Settings\Alice\Desktop\Database'. It is already opened exclusively by another user or you need permission to view its data.
I'm fairly new to databases, VBA and Access so any help would be greatly appreciated.
Also I have been told that the above method of having an Excel front-end and Access back-end is not recommended but alot of the analysis they conduct is done through Excel, and the charts feature in Excel is much better than Access in my experience atleast; and that is also one of the requirements for this project.
Thank you advance!
Solution to your first problem:
Sorry to be the bearer of bad news, but your entire first module is unnecessary. Instead, try:
Go to Data->Import External Data->Import Data, select your Access file, select your table, and presto! done!
Right-click on your new "External Data Range" to see a number of options, some related to formatting. You can even keep the original cell formatting and just update the values. I do this all the time.
To update the Excel data table later, there is a "External Data Range" toolbar that allows you to refresh it as well as a "refresh all" option to refresh every table in the Excel file. (You can also automate this thru code. It'll take some trial and error, but you're definitely up to the task)
Regarding your second problem
I've never used it, but there is also a "New Web Query" option in there as well. I assume it can be manipulated and updated the same way.
And lastly
Your choice of the Excel front-end and the Access back-end sounds good for your needs. It gets the data to your analysts in a medium they are familiar with (Excel) while keeping the calculations out of the way in Access. Technically, you could try putting all your calculations in Excel, but that might the Excel file much bigger and slower to open.
Do the data entry/updating/reviewing in Access. One of Access' strengths is using forms that allow you to update the tables without any code. Then allow the users to easily export the data to Excel such as by clicking on some command buttons.
Modules: Sample Excel Automation - cell by cell which is slow
Modules: Transferring Records to Excel with Automation
nothing wrong in principle with the excel/access pairing. I'm not familiar with ADO (I use DAO), but your error message seems to be indicating that the path to the datasource is not fully formed; or you already have it opened and hence are locking it.