Loop through recordset data 10 records at a time and add to array - vba

Context: I am writing a Google Maps API in my Microsoft Access database which takes postcodes in a table and sends them over to Google as part of a HTTP GET request. To make my requests more efficient, I am trying to batch 10 postcodes together at a time to send over to Google. I have chosen 10 as Google has a 2000 character limit in it's GET API requests.
Exam Question: Using DAO recordsets, how do I loop through my Postcodes table, 10 records at a time and add those 10 records to an array, until I reach the end of the table? So in essence, get 10 records from the table, add them to an array, then clear the array, then get the next 10 records and add them to the array, then clear the array...until I reach the end of the table.
My basic code so far is:
Public Function CalcGeoData()
Dim rs As DAO.Recordset
Dim Postcodes(0 To 10) As String
' Begin to loop through the Postcodes in I_Postcodes and update for distance and time from base
Set rs = CurrentDb.OpenRecordset("Postcodes")
If Not (rs.EOF And rs.BOF) Then
rs.MoveFirst
Do Until rs.EOF = True
Debug.Print (rs!Postcode)
Debug.Print "-----------"
rs.Move 10
Loop
End If
rs.Close
Set rs = Nothing
End Function

Inside your loop:
Create another loop from i = 1 to 10. Inside that loop:
Add the values from the current record to myArray(i).
Special case: If the end of the recordset is reached, clear the remaining array entries.
Send the array to Google (or do whatever else you want to do with it).
Translating this to code is left as an exercise.
Note: You might want to consider using a VBA Collection instead of an array, which has a useful Add method.

Use the GetRows method: Moving through the Recordset in Access VBA
Put it inside a loop where you pull 10 records (not RecordCount as shown) in each loop until no more records.

Related

Adding a New Element/Field with an Increment Integer as Value

After using Mongoimport to import a CSV file to my database, I want to add a new field or element per document. And, the data per for this new field is the is the index number plus 2.
Dim documents = DB.GetCollection(Of BsonDocument)(collectionName).Find(filterSelectedDocuments).ToListAsync.Result
For Each doc in documents
DB.GetCollection(Of BsonDocument)(collectionName).UpdateOneAsync(
Builders(Of BsonDocument).Filter.Eq(Of ObjectId)("_id", doc.GetValue("_id").AsObjectId),
Builders(Of BsonDocument).Update.Set(Of Integer)("increment.value", documents.IndexOf(doc) + 2).Wait()
Next
If I have over a million of data to import, is there a better way to achieved this like using UpdateManyAsync?
Just as a side note: Since you've got the Wait() and the Result everywhere, the Async methods don't seem to make an awful lot of sense. Also, your logic appears flawed since there is no .Sort() anywhere. So you've got no guarantee about the order of your returned documents. Is it indended that every document just gets a kind of random but unique and increasing number assigned?
Anyway, to make this faster, you'd really want to patch your CSV file and write the increasing "increment.value" field straight into it before the import. This way, you've got your value directly in MongoDB and do not need to query and update the imported data again.
If this is not an option you could optimize your code like this:
Only retrieve the _id of your documents - that's all you need and it will majorly impact your .find() perfomance since a lot less data needs to be transferred/deserialized from MongoDB.
Iterate over the Enumerable of your result instead of using a fully populated list.
Use bulk writes to avoid connecting to MongoDB again and again for every document and use a chunked flushing approach and flush every 1000 documents or so.
Theoretically, you could go further using multithreading or yield semantics for nicer streaming. However, that's getting a little complicated and may not even be needed.
The following should get you going faster already:
' just some cached values
Dim filterDefinitionBuilder = Builders(Of BsonDocument).Filter
Dim updateDefinitionBuilder = Builders(Of BsonDocument).Update
Dim collection = DB.GetCollection(Of BsonDocument)(collectionName)
' load only _id field
Dim documentIds = collection.Find(filterSelectedDocuments).Project(Function(doc) doc.GetValue("_id")).ToEnumerable()
' bulk write buffer (pre-initialized to size 1000 to avoid memory traffic upon array expansion)
Dim updateModelsBuffer = new List(Of UpdateOneModel(Of BsonDocument))(1000)
' starting value for our update counter
Dim i As Long = 2
For Each objectId In documentIds
' for every document we want one update command...
' ...that finds exactly one document identified by its _id field
Dim filterDefinition = filterDefinitionBuilder.Eq(Of ObjectId)("_id", objectId)
' ...and updates the "increment.value" with our running counter
Dim updateDefinition = updateDefinitionBuilder.Set(Of Integer)("increment.value", i)
updateModelsBuffer.Add(New UpdateOneModel(Of BsonDocument)(filterDefinition, updateDefinition))
' every e.g. 1000 documents
If updateModelsBuffer.Count = 1000
' we flush the contents to the database
collection.BulkWrite(updateModelsBuffer)
' and we empty our buffer list
updateModelsBuffer.Clear()
End If
i = i + 1
Next
' flush left over commands that have not been written yet in case we do not have a multiple of 1000 documents
collection.BulkWrite(updateModelsBuffer)

Access Calculate Next Value (lookup value}?

I need to get a value from a textbox in a report's detail section which will give me multiple values. I need to get each of those values into VB to do some calculations... I can pull a value from forms with ChildID = Forms!FRM_Child!ChildID.Value, but when I put
Private Sub Report_Open(Cancel As Integer)
Dim ChildID as Integer
ChildID = Reports!RPT_Due_Date!ChildID.Value
End Sub
it crashes and says "Run-time error '2424': The expression you entered has a field, control, or property name that Microsoft Access can't find."
I've checked and double checked the names. The thing I figure is that somehow because it's in the detail section with multiple values it crashes. Any ideas?
ChildID Last_Asmt_Type Last_Asmt_Date Next_Asmt_Type Next_Asmt_Date
1 Initial Evaluation 1/5/15 Periodic Review 5/5/15
2 Periodic Review 2/5/15 Annual Review 6/1/15
3 Annual Review 3/5/15 Periodic Review 7/1/15
What I want to do is get the Last_Asmt_Type and then with if/then rules select the Next_Asmt_Type ie If Last_Asmt_Type is Periodic Then Next_Asmt_Type is Annual....
How would I do this with a lookup value?
In Report_Open(), the textbox exists as control, but it has no value yet.
Your code would work in Report_Load(), but as you already know, it would be pointless because you would only get the first value.
If you want to read all values, don't try to read them from the report textbox, open a recordset on the report's data source. Like this:
Dim RS As Recordset
Set RS = CurrentDb.OpenRecordset(Me.RecordSource)
Do While Not RS.EOF
Debug.Print RS!MMI
RS.MoveNext
Loop
RS.Close
But:
Most probably there is an entirely different and better way to do what you want. What kind of calculations are you doing?

VBA collection with 2 or more fields

I'm using a Collection to store data from a recordset in VBA. The recordset has two fields.
I'm using a collection because I want to utilise its ability to prevent duplicates by using the key parameter. I'm running an SQL query to generate the recordset many times and a lot of the results will be identical to the previous, but some will be different. I want to capture a collection of the unique results from each recordset.
I can do this currently using the following:
rs.Open sql_vehicles, cn
If rs.RecordCount > 0 Then
Do While Not rs.EOF
On Error Resume Next
value = rs.Fields("EVN").value
catalogue_Tags.Add Item:=value, Key:=value
rs.MoveNext
On Error GoTo 0
Loop
End If
which all resides in a for loop generating a new recordset each time which may or may not be different.
This will give me a collection with unique values from the "EVN" field in the recordset, but I need to be able to store the second field in the recordset as well, but I want to still avoid duplicates of the EVN field!
Any ideas on how to do this?
Seems to me you can just use 2 collections to get 2 unique lists...
value = rs.Fields("EVN").value
catalogue_Tags_EVN.Add Item:=value, Key:=value
value = rs.Fields("ABC").value
catalogue_Tags_ABC.Add Item:=value, Key:=value
I just notice your requirement is slightly different then I read the first time...
Add "Microsoft Scripting Runtime" reference to your project and use a dictionary of dictionaries.
Dim evnValues as new Scripting.Dictionary ' this will actually be a dictionary of dictionaries.
inside the loop do this...
ABCvalue = rs.Fields("ABC").value
if not evnValues.Exists(value) then
' sub dictionary does not exist yet. initialize the list for this evn value
evnValues(value)=New Scripting.Dictionary
end if
evnValues(value).item(ABCValue)=ABCValue ' accumulate a list of the ABCValues relative to the unique evnValue.
At the end you will have a dictionary with the Names of the top level being your unique EVN values and the sub value will be a collection of 1 or more ABC values.

How to get every record in a Recordset in VB?

Hey here my problems i'm calling a stored proc and I put the record into an ADODB RecordSet.
When i Check the field.count its say i got 6 result which is good. But when i try to loop into that record set it skips one of them. Heres my code
oRs = g_oSQL.GetRecords("PS_palFetchAllPalette_sel")
If Not oRs Is Nothing Then
cmbPallet.Items.Clear()
oRs.MoveFirst()
While Not oRs.EOF
If oRs.Fields.Item("palcode").Value.ToString() <> "None" Then
cmbPallet.Items.Add(oRs.Fields.Item("palcode").Value.ToString())
End If
oRs.MoveNext()
End While
I think what happens is when you reach your last record with oRs.MoveNext() then you are at EOF so next loop won't happen. try to change the structure of the loop.
With oRs
Do Until .EOF
'get your data
.MoveNext
Loop
End With
I haven't worked with ADODB for a while but wouldn't field.count indicate the number of fields in the recordset and not the number of records returned? You could have returned 0 records but still have the metadata on the query.
Are you sure your check for "None" is working? This would require that palcode not ne empty or Null but have the actual value of "None." Set a breakpoint inside the While loop and see if your are getting what you expected.

Using GetRows on Recordset object returns no rows

I am trying to load a recordset into an array. I am using the following code:
Set rst = CurrentDb.OpenRecordset("SELECT id FROM TABLE1")
bankacid = rst.GetRows()
rst.Close
i = UBound(bankacid, 2)
MsgBox i + 1
This returns no rows. If I use "bankacid = rst.getrows(5)" it works.
I am very new to VBA and would very much
appreciate someone pointing out what I am missing.
It would be best to take a step back and say why you want an array, after all, a DAO recorset is much more functional than an array. If an array is really necessary, use ADODB. If you just want to refer to fields and rows, use Move, MoveFirst, MoveLast, MoveNext, MovePrevious and either the name of the field (column) or the ordinal position (.Field(3).