I created code for importing data from Excel into desired table, via TransferSheet and builded Query method. I'm also trying to resolve all errors that User could do when Importing data into db (wrong file format, appending 0 rows,field names not same as in DB etc.), but cannot get rid of Error 3059 "was unable to append all data to table" - It occurs when you try to Import some invalid data. I want a custom Msgbox for this error, and stop executing my Query. Here's my code - in short :
Private Sub CmdImport_Click()
Dim SQL As String
Dim dbs As DAO.Database
Set dbs = CurrentDb
On Error GoTo ERR1
DoCmd.TransferSpreadsheet acImport, acSpreadsheetTypeExcel12Xml, "NEWTABLE", "<Imported file>", True
SQL = " INSERT INTO MyTable (Field1)" & _
" SELECT DISTINCT" & _
" FROM NEWTABLE"
DoCmd.SetWarnings False
dbs.Execute SQL
DoCmd.RunSQL "DELETE * FROM NEWTABLE"
DoCmd.SetWarnings True
ERR1:
If Err.Number = 3059 Then
MsgBox "This file doesn't have proper data to import. Import canceled !"
Exit Sub
End If
End Sub
This code pops-up custom Msgbox after Access allready opens built-in window, regardless of DoCmd.SetWarnings False. If I move DoCmd.SetWarnings False before TransferSheet method, import gets executed and no Msgbox is displayed - which is wrong. How can I handle this error, anybody knows ??
You could import to a temp table.
Then read this with a query that converts and cleans the data, and use this query for your further processing - which now will run without errors caused by malformed data.
I have figured out another way to solve this. I have put all controls that I need before DoCmd.TransferSheet method, including eliminating error that causes "was unable to append all data to table". I added code for checking excel file, and If Excel file data doesn't match criteria, DoCmd.TransferSheet is not performed - so therefore error "was unable to append all data to table" doesn't appear at all. Here It is (part of code which first checks If Excel file data is proper to perform DoCmd.TransferSheet import) :
Dim XcelApp As Object
Dim x, i
Set XcelApp = CreateObject("Excel.Application")
XcelApp.ScreenUpdating = False
XcelApp.Workbooks.Open("C:\Users\Lucky\Desktop\Test\Sample.xlsx")
With XcelApp
i = XcelApp.Rows(1).Find(What:="Število", LookIn:=xlValues, Lookat:=xlWhole).Column
x = XcelApp.Range(XcelApp.Cells(1, i), XcelApp.Cells(XcelApp.Rows.Count, i).End(xlUp)).Value
For i = 2 To UBound(x)
If Not IsNumeric(x(i, 1)) Then
ExcelApp.Quit
Set ExcelApp = Nothing
MsgBox "This Excel file is not valid"
: Exit Sub
End If
Next i
End With
XcelApp.Quit
XcelApp = Nothing
Code is snapshop from this solved thread: loop through Excel columns
Related
I have an access db that pulls volumes from a table of exceptions. Each volume has an ID. I've created queries to pull details, for all possible volumes, and saved each one with the same name as each volume ID. Each time the volume exceptions are pulled into this db, the volume IDs can change. So, there is a query that runs that updates the volume table with the new IDs.
Unless you know a way to do this with a query, I need to write Access VBA code that will loop through the volume table, identify the name of each query and then run those queries until it reaches the end of the table. For example, the code needs to look at the first record in the volume table, say it is 1040. This is the name of the query that needs to run. The code then needs to find the query named 1040 and run it. It is a make table query.
The table name is FacilityVolume and it has one field named Volume. The value in the field is shorttext format even though it is numeric.
I've tried a couple of different things. Here is my latest try.
Dim db as Database
Dim vol as Recordset
Dim code as QueryDef
Set db = CurrentDb()
Set vol = db.OpenRecordset("FacilityVolume")
Set Volume = vol.Fields("Volume")
Vol.MoveFirst
Do Until vol.EOF = True
If QueryDef.Name = Volume Then
DoCmd.OpenQuery
Else MsgBox("The query does not exist")
vol.MoveNext
Loop
End Sub
I've searched the internet for a few days and can't find any reference to this particular code. I'm sure other users would like to know how to do this. I'm a novice and still learning VBA so any help you can provide is greatly appreciated.
Your code will loop through, even if you found your query and you do not pass the Query-Name to the OpenQuery command... This won't work...
The collection CurrentDb.QueryDefs knows all existing queries, but there is no "Exists" or "Contains" method.
So: The approach would be a loop (as you tried it) or an Error handling.
It's quite a time ago since I've coded with VBA, but I think you could try:
On Error Resume Next
DoCmd.OpenQuery "YourQueryName"
If Err Then
MsgBox("The query does not exist!")
Err.Clear
End If
On Error Goto 0
I recommend using full DAO in VBA to accomplish your goal. DoCmd.OpenQuery is really a VBA function that mimics the Macro RunQuery action. You don't get much control or true error handling capability.
Here is a complete code function that
Gives you an example of how to select all or some records from your table that lists the queries, including the ability to only select "Active" records, and even sort them in a particular execution sequence
Handles the instances where the query name in your table does not exist
Allows you to display a message about any errors that occur
Allows you to return an exit code to the calling procedure so that you can possibly act on the results of running these queries (such as choosing not to do the next step in your code if this function encounters an error of any kind (returns a non-zero value)
Here is the code. You will need to modify the SQL statement for your correct table name and field names, but this should be a good example to get you on your way.
Public Function lsProcessQuerySet() As Long
On Error GoTo Err_PROC
Dim ErrMsg As String
Dim db As DAO.Database
Dim rstEdits As DAO.Recordset
Dim qdf As DAO.QueryDef
Dim mssql As String
Dim ReturnCode As Long
Set db = CurrentDb()
'============================
'Select the list of Queries you want to process
'============================
mssql = "SELECT tblQueryList.ID, tblQueryList.QueryName, "
mssql = mssql & "tblQueryList.QueryShortDesc "
mssql = mssql & "FROM tblQueryList "
mssql = mssql & "WHERE tblQueryList.QueryActive = True "
mssql = mssql & "ORDER BY tblQueryList.SortOrder;"
Set rstEdits = db.OpenRecordset(mssql, dbOpenDynaset)
DoCmd.Hourglass True
'============================
'Execute each query, allowing processing to continue
'if the query does not exist (an error occurs)
'============================
Do While Not rstEdits.EOF
Set qdf = db.QueryDefs(rstEdits("QueryName"))
qdf.Execute dbSeeChanges
ResumeNextEdit:
rstEdits.MoveNext
Loop
rstEdits.Close
Exit_PROC:
lsProcessQuerySet = ReturnCode
Set qdf = Nothing
Set rstEdits = Nothing
db.Close
Set db = Nothing
DoCmd.Hourglass False
Exit Function
Err_PROC:
Select Case Err.Number
Case 3265 'Item Not Found in this Collection
ReturnCode = Err.Number
ErrMsg = "Query Not Found:" & vbCrLf & vbCrLf
ErrMsg = ErrMsg & rstEdits("QueryName")
DoCmd.Hourglass False
MsgBox ErrMsg, vbOKOnly + vbCritical, "Function lsProcessQuerySet"
Resume ResumeNextEdit
Case Else
ReturnCode = Err.Number
ErrMsg = "Error: " & Err.Number & vbCrLf
ErrMsg = ErrMsg & Err.Description
DoCmd.Hourglass False
MsgBox ErrMsg, vbOKOnly + vbCritical, "Function lsProcessQuerySet"
Resume Exit_PROC
End Select
End Function
The answer of #Shnugo is already good. Just to give you a complete VBA function, this should be working for you.
Public Sub MySub()
On Error GoTo err_mySub
Dim db as Database
Dim vol as Recordset
Set db = CurrentDb()
Set vol = db.OpenRecordset("FacilityVolume", dbOpenDynaset) ' I don't know if you want to run all queries of the table "FacilityVolume".
'So maybe you could also use something like "SELECT Volume FROM FacilityVolume WHERE Volume LIKE ""*10*"""
Vol.MoveFirst
Do Until vol.EOF = True
DoCmd.OpenQuery vol!Volume
vol.MoveNext
Loop
exit_MySub:
Exit Sub
err_MySub:
If Err.Number = 7874 Then
MsgBox "The Query """ & Vol!Volume & """ wasn't found."
Resume Next
Else
MsgBox Err.Description
Resume exit_MySub
End If
End Sub
Just to start with , I am not a very experienced programmer in Access. Is there any way I can disable the import error tables which are auto generated by access when you import files from excel ?
The reason I want to do this is my excel file has about 4000 rows with data about different locations,now the location I have to do the reporting on is importing properly thats why I am not worried about about the import errors . Also, it only detects the error in one row and because I import the table from vba code it will keep generating this error tables and I end up with big bunch of them.
I did some research but I find answer about solving the issue by fixing the file format of import , but I failed to get the answer about how to disable them.
Appreciate if anyone can help.
EDIT :
After suggestion from #parfait following code did the trick. Any other suggestions are also welcome.
Sub dropImportError()
Dim tbl_name As DAO.TableDef, str As String
With CurrentDb
For Each tbl_name In .TableDefs
str = tbl_name.Name
If InStr(str, "ImportErrors") <> 0 Then
str = "DROP TABLE" & str & ""
DoCmd.RunSQL str
End If
Next
End With
End Sub
Just came across this issue myself, after running the import of procedure I immediately ran the following code which was saved in a stand-alone Module. Simply paste the function DeleteImportErrors into the macro, procedure, or function.
Module code:
Public Function DeleteImportErrors()
Dim n As Integer
Dim db As DAO.Database
Set db = CurrentDb
For n = db.TableDefs.Count - 1 To 0 Step -1
' loop through all tables
If InStr(1, db.TableDefs(n).Name, "ImportError") > 0 Then
' if table is import errors table
DoCmd.DeleteObject acTable, db.TableDefs(n).Name
' delete table
End If
Next n
End Function
'Delete Import Error tables
Private Sub Delete_Error_Tables()
Do Until IsNull(DLookup("Name", "Msysobjects", "Name like '*ImportErrors*'"))
DoCmd.DeleteObject acTable, DLookup("Name", "Msysobjects", "Name like '*ImportErrors*'")
Loop
End Sub
Frequently I drop ready-made queries into Access. Create > Query Design > SQL, and paste the code directly to the text window.
Generally I do not save these queries in Access because I have to minimize the clutter of one-time, ad hoc reporting. I wrote a macro for Access that will automatically save the results of an established query...
Sub qry40T_export()
'export the results of the query "qry40T" to local excel file
'prompt the user for the save location
'name the file "qry40T_output.xls"
'initialize variable type
Dim save_dir As String
save_dir = "dunno_yet"
'initialize default filename
savefile_name = "qry40T_output.xls"
'prompt user for save location
save_dir = InputBox(Prompt:="Save query export to the following directory:", Title:="Save file to:", _
Default:="F:\QUERYDATA\")
'validate user submitted
If save_dir = "" Then
'user chose 'Cancel'
Exit Sub
End If
'compose full save filename
fullsavefile_name = save_dir & savefile_name
'edit error treatment
On Error GoTo ErrHandler
'export the query
'overwrite "qry40T_output.xls"
DoCmd.OutputTo acOutputQuery, "qry40T", "Excel97-Excel2003Workbook(*.xls)", fullsavefile_name, False, "", , acExportQualityPrint
'success
MsgBox ("Export successful.")
'restore error treatment
On Error GoTo 0
'error handling resolution
subexit:
Exit Sub
'error handling message
ErrHandler:
MsgBox Error$
Resume subexit
End Sub
...but now I would like to apply this same process to an unsaved query. Is that possible? My guess is that the code would look something like this: DoCmd.OutputTo acOutputQuery, OpenQuery(1), "Excel97-Excel2003Workbook(*.xls)", fullsavefile_name, False, "", , acExportQualityPrint, but I can't seem to find the right syntax.
Since these are 'throw-away' queries and don't need to be saved, you could save them with the same query name every time.
For example, you can always save your temporary queries as "qry40T". Then, your macro will always work and save the results of whatever query is saved in qry40T at the time.
I am trying to import all my spreadsheets in a workbook to Access. However, nothing gets imported into Access even though i receive no error message. Everything is working except for the line noted below, where even though it seems like Access is importing the spreadsheets, nothing appears in my table.
Public Sub Import_Excel_Workbook()
Dim strFile As String
Dim StrFldrPath As String
Dim strfilelist() As String
Dim intFile As Integer
Dim filename As String
DoCmd.SetWarnings False
StrFldrPath = "C:\Documents\SPY\New\"
'Loop through the folder & build file list
strFile = Dir(StrFldrPath & "*.xls")
' (commented-out code removed for clarity)
Set objAccess = CreateObject("Access.Application")
objAccess.OpenCurrentDatabase "C:\Documents\Database2.accdb" 'not dynamic yet
Set objExcel = CreateObject("Excel.Application")
objExcel.Visible = True
Set objWorkbook = objExcel.Workbooks.Open("C:\Documents\SPY\New\SPY_1.xls") 'not dynamic yet
Set colWorksheets = objWorkbook.Worksheets
'cycle through the list of files
'For intFile = 1 To UBound(strfilelist)
'filename = StrFldrPath & strfilelist(intFile) (removed for the time being)
For Each objWorksheet In colWorksheets
Set objRange = objWorksheet.UsedRange
strWorksheetName = objWorksheet.Name & "!" & objRange.Address(False, False)
'########## LINE BELOW SEEMS TO FAIL ############
objAccess.DoCmd.TransferSpreadsheet acImport, acSpreadsheetTypeExcel9, _
"S&P", "C:\Documents\SPY\New\SPY_1.xls", True, strWorksheetName 'not dynamic yet
Next
'Next intFile
DoCmd.SetWarnings True
End Sub
It looks like you are trying to follow this approach: http://blogs.technet.com/b/heyscriptingguy/archive/2008/01/21/how-can-i-import-multiple-worksheets-into-an-access-database.aspx. It's important to note this technique lets you import all tabs (worksheets).
As suggested in a comment, you must avoid setting SetWarnings to False. You can't trouble shoot when you are suppressing errors.
Your post has a lot of code that doesn't relate to your issue. Please check https://stackoverflow.com/help/mcve for how to ask in a way that encourages answers.
Having said all that, I suggest you change your tab (worksheet) name so it does not contain the symbol &.
If that does not solve your problem, try using the Access import wizard on your problem tab . If you don't have experience with imports, be aware there are many "gotchas". Your source document needs to be free of merged cells, incoherent header-row entries, and countless other snags. The import wizard may reveal some problem with the source data.
Finally, if the source worksheet is empty (none of the cells have values), the import will halt at that point. (This is not the behavior you are reporting, but it's worth a mention.)
I'm working on a Access database which generates some mails with mail merge called from VBA code in the Access database. The problem is that if I open a new Word document and start the mail merge (VBA), Word opens the same Access database (which is already open) to get the data. Is there any way to prevent this? So that the already opened instance of the database is used?
After some testing I get a strange behavior: If I open the Access database holding the SHIFT-Key the mail merge does not open an other Access instance of the same database. If I open the Access database without holding the key, I get the described behavior.
My mail merge VBA code:
On Error GoTo ErrorHandler
Dim word As word.Application
Dim Form As word.Document
Set word = CreateObject("Word.Application")
Set Form = word.Documents.Open("tpl.doc")
With word
word.Visible = True
With .ActiveDocument.MailMerge
.MainDocumentType = wdMailingLabels
.OpenDataSource Name:= CurrentProject.FullName, ConfirmConversions:=False, _
ReadOnly:=False, LinkToSource:=False, AddToRecentFiles:=False, _
PasswordDocument:="", PasswordTemplate:="", WritePasswordDocument:="", _
WritePasswordTemplate:="", Revert:=False, Format:=wdOpenFormatAuto, _
SQLStatement:="[MY QUERY]", _
SQLStatement1:="", _
SubType:=wdMergeSubTypeWord2000, OpenExclusive:=False
.Destination = wdSendToNewDocument
.Execute
.MainDocumentType = wdNotAMergeDocument
End With
End With
Form.Close False
Set Form = Nothing
Set word = Nothing
Exit_Error:
Exit Sub
ErrorHandler:
word.Quit (False)
Set word = Nothing
' ...
End Sub
The whole thing is done with Access / Word 2003.
Update #1
It would also help if someone could tell me what the exact difference is between opening Access with or without the SHIFT-Key. And if it is possible to write some VBA code to enable the "features" so if the database is opened without the SHIFT-Key, it at least "simulates" it.
Cheers,
Gregor
When I do mailmerges, I usually export a .txt file from Access and then set the mail merge datasource to that. That way Access is only involved in exporting the query and then telling the Word document to do the work via automation, roughly as follows:
Public Function MailMergeLetters()
Dim pathMergeTemplate As String
Dim sql As String
Dim sqlWhere As String
Dim sqlOrderBy As String
'Get the word template from the Letters folder
pathMergeTemplate = "C:\MyApp\Resources\Letters\"
'This is a sort of "base" query that holds all the mailmerge fields
'Ie, it defines what fields will be merged.
sql = "SELECT * FROM MailMergeExportQry"
With Forms("MyContactsForm")
' Filter and order the records you want
'Very much to do for you
sqlWhere = GetWhereClause()
sqlOrderBy = GetOrderByClause()
End With
' Build the sql string you will use with this mail merge
sql = sql & sqlWhere & sqlOrderBy & ";"
'Create a temporary QueryDef to hold the query
Dim qd As DAO.QueryDef
Set qd = New DAO.QueryDef
qd.sql = sql
qd.Name = "mmexport"
CurrentDb.QueryDefs.Append qd
' Export the data using TransferText
DoCmd.TransferText _
acExportDelim, , _
"mmexport", _
pathMergeTemplate & "qryMailMerge.txt", _
True
' Clear up
CurrentDb.QueryDefs.Delete "mmexport"
qd.Close
Set qd = Nothing
'------------------------------------------------------------------------------
'End Code Block:
'------------------------------------------------------------------------------
'------------------------------------------------------------------------------
'Start Code Block:
'OK. Access has built the .txt file.
'Now the Mail merge doc gets opened...
'------------------------------------------------------------------------------
Dim appWord As Object
Dim docWord As Object
Set appWord = CreateObject("Word.Application")
appWord.Application.Visible = True
' Open the template in the Resources\Letters folder:
Set docWord = appWord.Documents.Add(Template:=pathMergeTemplate & "MergeLetters.dot")
'Now I can mail merge without involving currentproject of my Access app
docWord.MailMerge.OpenDataSource Name:=pathMergeTemplate & "qryMailMerge.txt", LinkToSource:=False
Set docWord = Nothing
Set appWord = Nothing
'------------------------------------------------------------------------------
'End Code Block:
'------------------------------------------------------------------------------
Finally:
Exit Function
Hell:
MsgBox Err.Description & " " & Err.Number, vbExclamation, APPHELP
On Error Resume Next
CurrentDb.QueryDefs.Delete "mmexport"
qd.Close
Set qd = Nothing
Set docWord = Nothing
Set appWord = Nothing
Resume Finally
End Function
To use this, you need to set up your Resources\Letters subfolder and put your mailmerge template word file in there. You also need your "base" query with the field definitions in your Access App (in the example, it is called MailMergeExportQry. But you can call it anything.
You also need to figure out what filtering and sorting you will do. In the example, this is represented by
sqlWhere = GetWhereClause()
sqlOrderBy = GetOrderByClause
Once you have got your head round those things, this is highly reusable.