Skip recordset in loop and continue to next - ms-access

I inherited an Access program that reads Excel files in a directory and brings them in to Access in a For/Each Ofile loop using DoCmd.TransferSpreadsheet acLink and Set rs1=db.openrecordset.
It then goes into a loop: Do While not rs1.EOF.
In the loop it immediately checks the first two fields (ID and ID2) for correct formatting.
The previous code would just cancel out of the entire loop if there were any errors in the formatting and skip to the top of the For/Each loop and read the next spreadsheet.
I would like to test those two fields but skip to the top of the Do While loop if there is no error. I can't seem to do this. So I want to skip the recordset that is in error but read the next one.
My code is below. I've skipped the For/Each loop since it just reads the next Excel file. The following code is within the For/Each loop.
set rs2 = db.openrecordset("tblIn_Access")
source="C:\Documents\Test_for_import\" & oFile.name
doCMD.TransferSpreadsheet acLink, 10, ofile.name, source, true
Set rs1=db.openrecordset("Select * from " & ofile.name & " ;")
Do While not rs1.eof
IDVal=rs1.fields(0).value
ID2Val=rs1.fields(1).value
if len(idVal) < 9 then
'Here is where I want to stop and read the next record in the spreadsheet.
' I don't want any more processing to occur
if left(id2Val) = "H" then
'This is another case when I want to stop and read the next record
' and not do any more processing on the record
'If these two errors do not occur then I want to continue with the next record
'There is a lot of data manipulation here to get fields in the right format.
' Once they are correct I add the values to a table in Access
rs2.AddNew
rs2.("MBR_ID")=idval
rs2.("ORD_ID")=id2val
rs2.("DATE_IN")=dateinval
rs2.("DATE_OUT")=dateoutval
rs2.Update
rs2.movenext
rs1.movenext
Loop
I can't get the processing to stop on the first two fields and go back and read the next record if they are not correctly formatted. The only thing I've been able to do is what the code originally did, stop and read in the next Excel sheet. There is a lot of manipulation in the code following the checks on ID & ID2 but I only want that code to run if the two fields are in the proper format.

This is time for the otherwise unused 'GOTO command.
set rs2 = db.openrecordset("tblIn_Access")
source="C:\Documents\Test_for_import\" & oFile.name
doCMD.TransferSpreadsheet acLink, 10, ofile.name, source, true
Set rs1=db.openrecordset("Select * from " & ofile.name & " ;")
Do While not rs1.eof
IDVal=rs1.fields(0).value
ID2Val=rs1.fields(1).value
if len(idVal) < 9 then GOTO SkipRecord
if left(id2Val) = "H" then GOTO SkipRecord
rs2.AddNew
rs2.("MBR_ID")=idval
rs2.("ORD_ID")=id2val
rs2.("DATE_IN")=dateinval
rs2.("DATE_OUT")=dateoutval
rs2.Update
SkipRecord:
rs2.movenext
rs1.movenext
Loop

Related

Access - custom "was unable to append all data to table"?

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

DoCmd.DeleteObject acTable - "could not lock table because it is already in use"

I'm getting the following error when trying to delete a table:
Run-time error '3211'
The database engine could not lock table 'RuleViolations1516' because
it is already in use by another person or process.
Here is the offending procedure, with a comment showing the line throwing the error:
Public Sub ImportRuleViolations()
DoCmd.Close acForm, "frmImportRuleViolations"
If _
TableExists("RuleViolations1516") = True _
Then
Debug.Print "Table RuleViolations1516 already exists"
DoCmd.DeleteObject acTable, "RuleViolations1516" ' <-- EXECUTION STOPS HERE
Debug.Print "...old table deleted..."
DoCmd.TransferSpreadsheet acTable, _
10, _
"RuleViolations1516", _
Forms!frmImportRuleViolations.txtRuleViolationsPath & Forms!frmImportRuleViolations.txtRuleViolationFile, _
-1
Debug.Print "...new data imported."
ElseIf _
TableExists("RuleViolations1516") = False _
Then
Debug.Print "Table RuleViolations1516 does not already exist"
DoCmd.TransferSpreadsheet acTable, _
10, _
"RuleViolations1516", _
Forms!frmImportRuleViolations.txtRuleViolationsPath & Forms!frmImportRuleViolations.txtRuleViolationFile, _
-1
Debug.Print "...new data imported."
End If
Dim db As DAO.Database
Dim tDef As TableDef, fld As DAO.Field
Set db = CurrentDb
db.TableDefs.Refresh
' LRN
Set tDef = db.TableDefs("RuleViolations1516")
Set fld = tDef.CreateField("newLRN", dbText, 20)
fld.OrdinalPosition = 2
tDef.Fields.Append fld
db.Execute _
"UPDATE RuleViolations1516 Set newLRN=[Learner Ref]", dbFailOnError
' delete old field
tDef.Fields.Delete "Learner Ref"
tDef.Fields.Refresh
' rename new field
tDef.Fields("newLRN").name = "LRN"
tDef.Fields.Refresh
Set fld = Nothing
Set tDef = Nothing
' AimRef
Set tDef = db.TableDefs("RuleViolations1516")
Set fld = tDef.CreateField("newAimRef", dbText, 20)
fld.OrdinalPosition = 7
tDef.Fields.Append fld
db.Execute _
"UPDATE RuleViolations1516 Set newAimRef=[Aim Reference Number]", dbFailOnError
' delete old field
tDef.Fields.Delete "Aim Reference Number"
tDef.Fields.Refresh
' rename new field
tDef.Fields("newAimRef").name = "AimRef"
tDef.Fields.Refresh
Set fld = Nothing
Set tDef = Nothing
Set db = Nothing
DoCmd.OpenForm "frmImportRuleViolations"
End Sub
The offending sub also makes reference to another function:
Public Function TableExists(name As String) As Boolean
TableExists = DCount("*", "MSysObjects", "Name = '" & name & "' AND Type = 1")
End Function
The above sub and function run on their own separate module (not tied to a form module).
The table RuleViolations1516 is not open when I run the sub. The form frmImportRuleViolations uses the RuleViolations1516 table in some queries behind some subforms, but as you can see from the sub, I've closed this form on the first line.
Any pointers would be appreciated.
Update:
frmImportRuleViolations has 2 subforms on it... removing them (temporarily) stops the issue. I need the subforms on the form though.. how can I get around this?
As I understand your updated question, you have a form called frmImportRuleViolations and that has a subform I'll call frmImportRuleViolationsSubform1.
frmImportRuleViolationsSubform1 references the RuleViolations1516 table somewhere on it.
I'm assuming you are also triggering this reload from a button or other control on frmImportRuleViolations. If so, then that makes sense as the cause of your error. Access may not have given up every reference to RuleViolations1516 by the time it gets to the close event.
That and a form closing and reopening itself can get kind of hairy.
One fairly easy thing to do would be to drop and load the table before the frmImportRuleViolationsSubform1 is opened. As a user (and a developer) that's what I would expect - I open the form and it's right up to date. And worst case situation closing the form and re-opening it gives me a refresh.
Failing that (it absolutely has to be reloaded after the form is closed); then I would make whatever is closing it open frmImportRuleViolations as a modal window, and then put reload of the table code after the call to frmImportRuleViolations since it will wait until control is returned to the calling window.
You could also instead load the data into a temporary staging table, and then delete the contents of RuleViolations1516 and repopulate it from the staging table. This would probably be the route I would take as it would eliminate the closing and opening of the frmImportRuleViolations form. You'd just have to tell the table on the sub-form to refresh (which I'll leave an an exercise for the reader).
Aside from that, a couple style notes:
'' this
If _
TableExists("RuleViolations1516") = True _
Then
'' could be equally written as
If TableExists("RuleViolations1516") Then
'' the "= True" is implied and not required
And
'' this
ElseIf _
TableExists("RuleViolations1516") = False _
Then
'' is redundant to the previous if. A simple else would do, since we
'' know if the last time it ran, it wasn't true, it must be false
And the DoCmd block looks like it does the same thing in both the if and else, so I'd be inclined to extract it like this:
If TableExists("RuleViolations1516") then
Debug.Print "Table RuleViolations1516 already exists"
DoCmd.DeleteObject acTable, "RuleViolations1516"
Debug.Print "...old table deleted..."
else
Debug.Print "Table RuleViolations1516 does not already exist"
end if
DoCmd.TransferSpreadsheet acTable, _
10, _
"RuleViolations1516", _
Forms!frmImportRuleViolations.txtRuleViolationsPath & _
Forms!frmImportRuleViolations.txtRuleViolationFile, _
-1
Debug.Print "...new data imported."
That way when you come back to change the name of the file (or whatever) you are only changing it in one place, etc. and goes along with the DRY principle - "DRY - don't repeat your self".
I like the use of underscores so that what you have coded does not run off the edge of the screen like some extract from war and peace. It makes it much more readable.
What you've done is not wrong. It's just not what most developers would do. And as much as we write code for the computer to function the way we want, we also want the next developer to touch your code 5 years down the line to easily understand what you are doing. And code always lasts longer than you think it will :^)
Good luck!

Difference between objRecordset.MoveNext and loop?

My code reads in lines of a .csv file (comma separated file) and assigns each value to a local variable. After I have done some logic and stored the values in a table, I read in the next line of the .csv file and repeat the process. My question is what is the objRecordset.MoveNext doing compared to the loop. Here is my code, some of it has been removed to focus on the loop and objrecordset.MoveNext.
objconnection.Open 'connection string'
objRecordset.Open "SELECT * FROM [" & ThisFileName & "]", objconnection, adOpenStatic, adLockOptimistic, adCmdText 'select all text lines from the file
Do While Not objRecordset.EOF 'read lines until end of file
'Clear out all the local objects so prior values aren't left there
SampleName = ""
DateTimeAcquired = ""
Analyte = ""
Concentration = ""
Units = ""
'reads in each value according to column name and save to variable'
SampleName = objRecordset.Fields("Sample Name").Value
DateTimeAcquired = objRecordset.Fields("Date and Time Acquired").Value
Analyte = objRecordset.Fields("Element Full Name").Value
Concentration = objRecordset.Fields("Concentration").Value
Units = objRecordset.Fields("Units").Value
'Logic done on variables'
objRecordset.MoveNext
Loop
I am using Access 2010 VBA
objRecordset.MoveNext serves two purposes
As an Exit condition
Move to Next record
If you don't have objRecordset.MoveNext then the loop will continue infinitely since it doesn't reach objRecordset.EOF and rather stays on same record

"Not a valid bookmark" with DAO Recordset

I'm in the process of converting an Access Data Project (ADP) into a standard ACCDB format with ODBC linked tables. In the ADP, I had overridden the Refresh button to return the user to the current record by using the following code:
Public Sub RibbonCmd_RefreshScreen(ctl As IRibbonControl, ByRef cancelDefault)
On Error GoTo ErrHandler
cancelDefault = False
DoCmd.Echo False
Dim saveBookmark
With Screen.ActiveForm
saveBookmark = .Bookmark
.Requery
.Bookmark = saveBookmark
End With
'Success - cancel the default behavior
cancelDefault = True
ExitHandler:
DoCmd.Echo True
Exit Sub
ErrHandler:
cancelDefault = False
Resume ExitHandler
End Sub
My understanding is that this should work just fine with DAO, but I get error 3159, Not a valid bookmark. I've also tried replacing .Bookmark with .Recordset.Bookmark, but that gave me the same result. Is there something I'm doing wrong here?
Actually, a requery of a form or a requery of a recordset will re-set and invalidate book marks.
So such book marks are no longer valid after a requery.
So the best approach here will depend on either
a) I simply want to re-display any changed records (and not move off current record).
b) I simply want to re-display any changed records AND ALSO display new records (the new records is the critical part).
If you just need a refresh, then you can use the appropriately called command refresh.
Eg:
Me.Refresh
Or in your case
Screen.ActiveForm.Refresh
So the above is ONE line of code and is ALL you need. The current record pointer for the form does NOT change when you use this command. All and any record changed will re-display for you.
Note that since you can behind the form button use:
Me.Refresh
Then LITTLE need is required to call a general routine as you have written.
However, if you need the form to "load" or display any new records added, then you DO have to use requery. In this case as noted book marks in this case all become invalid.
So, for code to requery, then we use the PK value (and hopefully you used the default pk of ID that been the default for 20 years). The code would then become:
Dim lngID As Long
If IsNull(Me!ID) Then Exit Sub
lngID = Me!ID
Me.Requery
Me.Recordset.FindFirst "id = " & lngID
Now of course if the PK id is not the same for each form, then you most certainly could pass the NAME of the PK value to your "general" refresh routine. It would look like:
Public Sub MyRefresh(strPK As String)
Dim lngID As Long
If IsNull(Me(strPK)) Then Exit Sub
lngID = Me(strPK)
Me.Requery
Me.Recordset.FindFirst strPK & " = " & lngID
End Sub
The "hope" here is you actually really JUST need refresh, since as noted this is only one line of code, and better yet it does NOT move the record pointer.
I use VB6 and Visual Data Manager in development. I have had the same problem. Most probably it arose when 2 users tried to update the same record in the same time. So some fields in the table are corrupted.
Here are the steps I used to solve the problem:
1- Copy the structure of the table (lets call it table1)to another table (lets call it table2).
2- Find the correpted record(s) in table1.
3- Transfer the data from table1 to table2 except the corrupted record(s)
4- Reenter the excluded record(s) to table2 again.
5- Rename table1 table3
6- Rename table2 table1
That's all folk
abdobox#yahoo.com
I have used the forms Recordset.AbsolutePosition, and this works fine e.g. in the OnKeyDown exit of a field
Dim PrefilterPosition As Long
Private Sub ValnSubject_KeyDown(KeyCode As Integer, Shift As Integer)
' Not F2 - exit
If KeyCode <> vbKeyF2 Then Exit Sub
' Get the active control
Dim ActiveCtl As Control
Set ActiveCtl = Me.ActiveControl
ActiveControlName = ActiveCtl.Name
' Is the form's filter set?
If Me.Filter = "" Then
' NO: Apply the new filter
' Note the current position in the recordset
PrefilterPosition = Me.Recordset.AbsolutePosition
' Set the filter to the Active control's value
Me.Filter = "[" & ActiveCtl.ControlSource & "]='" & ActiveCtl.Value & "'"
Me.FilterOn = Me.Filter <> ""
Me.Requery
Else
' YES: Clear the filter
Me.Filter = ""
Me.FilterOn = Me.Filter <> ""
Me.Requery
' Align the recordset on the previously stored position
Me.Recordset.AbsolutePosition = PrefilterPosition
End If
' Restore the cursor to where it came from
Me.Controls(ActiveControlName).SetFocus
Ex_it:
End Sub
For context: this code was from an idea for an 'Instant Filter', where you position the cursor on a field in a tab form, press F2, and then a filter is applied so you see only records with the selected field's value. Press F2 again and the filter is removed and the cursor goes back into the place it was when you hit F2 the first time. Bookmarks do not work here, as Albert says above.

How to read whole line from txt file with row delimiter?

I have text file with row, which can have several row separators. How to divide this big row into several rows using VBA?.
I need to import this file. I do so (Sep is the field delimiter):
Open FName For Input Access Read As #1
While Not EOF(1)
Line Input #1, WholeLine
If Right(WholeLine, 1) <> Sep Then
WholeLine = WholeLine & Sep
End If
StrArray = Split(WholeLine, Sep)
Wend
or may be you can suggest me another way to import data from txt file with field and row separators?
Well you have a lot of things in your question that need clarification, but maybe this will help you in the right direction. I am going to assume you are appending this to a table.
You'll have to change the following things for your scenario:
1. ImportTableName
2. FieldName
3. FileName
4. sDelimiter - probably this too.
also don't forget to add some error handling. If you drop out without closing the file, it stays locked and will affect your attempts at retrying the import.
Const sDelimiter As String = " sep "
Dim rsImport As ADODB.Recordset
Dim vShortLine As Variant
Dim arrLongLine As Variant
Dim sLongLine As String
Set rsImport = New ADODB.Recordset
With rsImport
.Open ImportTableName, CurrentProject.Connection, adOpenStatic, adLockOptimistic
Open FileName For Input As #1 ' Open file.
While Not EOF(1)
Line Input #1, sLongLine
arrLongLine = Split(sLongLine, sDelimiter)
For Each vShortLine In arrLongLine
.AddNew
!FieldName = vShortLine
Next
Wend
Close #1 ' Close file.
.UpdateBatch
.Close
End With
Set rsImport = Nothing