I'm writing a script that will take data from text reports generated by a legacy system and import it into a more flexible tracking database. I'm trying to figure out how to build up a dictionary from data within a table so that I can quickly filter through the imported data using the dictionary exists method and only import the data belonging to my shop.
I have a table called tblShop that contains a field called [WorkcenterID]. As my script runs, it's going to be extracting data from the text report, looking at the employee's work center, checking if that employee belongs to me (by comparing the extracted work center text to the dictionary), and if true, writes the data to an import table for further processing.
How do I build the dictionary from the table?
For the curious, here's my code:
Private Sub Command5_Click()
Dim crscd, startdt, stopdt, starttm, stoptm, bldstr, rmstr, evtid, empn, empw As String
Dim i, cd, ci, es, ee As Integer
Dim cdb As DAO.Database
Dim imt, sht As DAO.Recordset
Dim wcDict As New Scripting.Dictionary
Set cdb = CurrentDb
Set imt = cdb.OpenRecordset("tblImport", dbOpenTable)
Set sht = cdb.OpenRecordset("tblShop", dbOpenTable)
'--- grab pasted text data from form ---
strText = Me.InData.Value
'--- split text data into array so we can read line by line ---
arrlines = Split(strText, vbCrLf)
'--- reset line counters ---
i = 0
ci = -1
cd = -1
es = -1
ee = -1
For Each strline In arrlines
'--- find location of course info ---
If Left(strline, 17) = "COURSE NARRATIVE" Then
cd = i + 2
End If
'--- find location of course location info & event ID
If Left(strline, 8) = "BUILDING" Then
ci = i + 1
End If
'--- find where assigned employee data starts
If Left(strline, 6) = "EMP NR" Then
es = i + 1
End If
'--- find where assigned employee data ends
If es > 0 And IsNumeric(Left(strline, 5)) = False Then
ee = i - 1
End If
'--- extract course code and start/stop dates/times
If i = cd Then
crscd = Left(strline, 6)
startdt = Left(Right(strline, 28), 7)
starttm = Left(Right(strline, 20), 4)
stopdt = Left(Right(strline, 15), 7)
stoptm = Left(Right(strline, 7), 4)
End If
'--- extract building number, room number and event ID
If i = ci Then
bldstr = Trim(Left(strline, 13))
rmstr = Trim(Left(Right(strline, 55), 9))
evtid = Trim(Left(Right(strline, 46), 9))
End If
i = i + 1
Next
'--- clear import table
DoCmd.SetWarnings False
DoCmd.RunSQL "DELETE * FROM tblImport"
DoCmd.SetWarnings True
'--- dump employee data to import table
For n = es To ee
empn = Left(Left(arrlines(n), 48), 5)
empw = Left(Right(Left(arrlines(n), 48), 11), 4)
'--- verfiy employee belongs to us before importing data
'If wcDict.exists(empw) = True Then
'imt.AddNew
'imt!EmpID = empn
'imt!Workcenter = empw
'imt.Update
'End If
Next
wcDict = Nothing
imt.Close
Set imt = Nothing
sht.Close
Set sht = Nothing
cdb.Close
Set cdb = Nothing
End Sub
The solution is to use the following code to loop through the recordset to extract the required data:
sht.MoveFirst
Do While Not sht.EOF
empw = sht![WorkcenterID]
wcDict.Add empw, vbNullString
sht.MoveNext
Loop
empw = ""
Insert that before the pasted text data grab, uncomment the verify if block at the bottom and it'll work.
Related
I'm attempting to write a loop in VBA for Access 2010, where the loop looks through a table (table: "SunstarAccountsInWebir_SarahTest") and evaluates a number of conditions, and depending on the condition - may then loop through a different table ("1042s_FinalOutput_7") to see if it has an ID that matches. If it does match, it inserts "Test" into a field, if not - it should export that row of values (from the first loop - out of "SunstarAccountsInWebir_SarahTest") into an excel file.
My issue is that my code is exporting the entirety of the table "SunstarAccountsInWebir_SarahTest", I only want it to export the row corresponding to the value of i in the loop. How can I amend my code to do this?
Public Sub EditFinalOutput2()
'set loop variables
Dim i As Long
Dim qs As DAO.Recordset
Dim ss As DAO.Recordset
Dim strSQL As String
Dim external_nmad_id As String
Dim IRSfileFormatKey As String
'Function GetID(external_nmad_id As String, IRSfileFormatKey As String)
'open reference set
Set db = CurrentDb
Set qs = db.OpenRecordset("SunstarAccountsInWebir_SarahTest")
Set ss = db.OpenRecordset("1042s_FinalOutput_7")
'set loop for whole recordset(this is the original location, will try putting it within the If, ElseIf loop)
'For i = 0 To qs.RecordCount - 1
With qs.Fields
For i = 0 To qs.RecordCount - 1
If (IsNull(!nmad_address_1) Or (!nmad_address_1 = !nmad_city) Or (!nmad_address_1 = !Webir_Country) And IsNull(!nmad_address_2) Or (!nmad_address_2 = !nmad_city) Or (!nmad_address_2 = !Webir_Country) And IsNull(!nmad_address_3) Or (!nmad_address_3 = !nmad_city) Or (!nmad_address_3 = !Webir_Country)) Then
MsgBox "This was an invalid address"
Else:
With ss.Fields
For j = 0 To ss.RecordCount - 1
If (qs.Fields("external_nmad_id") = Right(ss.Fields("IRSfileFormatKey"), 10)) Then
ss.Edit
ss.Fields("box13_Address") = "Test"
ss.Update
Else: DoCmd.TransferSpreadsheet acExport, 10, "SunstarAccountsInWebir_SarahTest", "\\DTCHYB-MNMH001\C_WBGCTS_Users\U658984\My Documents\pre processor\PreProcessor7\ToBeReviewed\AddressesNotActiveThisYear.xlsx", False
End If
ss.MoveNext
Next j
End With
End If
qs.MoveNext
Next i
End With
'close reference set
qs.Close
Set qs = Nothing
ss.Close
Set ss = Nothing
End Sub
This ended up being the closest. I needed to switch to a "Do While" loop rather than a second integer loop. The code for so is below:Public Sub EditFinalOutput2()
'set variables
Dim i As Long
Dim qs As DAO.Recordset
Dim ss As DAO.Recordset
Dim strSQL As String
Dim external_nmad_id As String
Dim IRSfileFormatKey As String
Dim mytestwrite As String
mytestwrite = "No"
'open reference set
Set db = CurrentDb
Set qs = db.OpenRecordset("SunstarAccountsInWebir_SarahTest")
Set ss = db.OpenRecordset("1042s_FinalOutput_7")
With qs.Fields
For i = 0 To qs.RecordCount - 1
If (IsNull(!nmad_address_1) Or (!nmad_address_1 = !nmad_city) Or
(!nmad_address_1 = !Webir_Country) And IsNull(!nmad_address_2) Or (!nmad_address_2 =
!nmad_city) Or (!nmad_address_2 = !Webir_Country) And IsNull(!nmad_address_3) Or
(!nmad_address_3 = !nmad_city) Or (!nmad_address_3 = !Webir_Country)) Then
DoCmd.RunSQL "INSERT INTO Addresses_ToBeReviewed SELECT
SunstarAccountsInWebir_SarahTest.* FROM SunstarAccountsInWebir_SarahTest WHERE
(((SunstarAccountsInWebir_SarahTest.external_nmad_id)='" & qs!external_nmad_id &
"'));"
Else:
Set ss = db.OpenRecordset("1042s_FinalOutput_7")
With ss.Fields
'if not invalid address, loop through second (final output) table to find
matching ID's
If ss.EOF = False Then
ss.MoveFirst
Do
Dim mykey As String
mykey = Right(ss!IRSfileFormatKey, 10)
Debug.Print mykey
If qs.Fields("external_nmad_id") = mykey Then
ss.Edit
ss.Fields("box13c_Address") = qs.Fields("nmad_address_1") &
qs.Fields("nmad_address_2") & qs.Fields("nmad_address_3")
ss.Update
mytestwrite = "Yes"
End If
ss.MoveNext
'if the valid address doesn't match to final output table, add to list of
addresses not matched
Loop Until ss.EOF
If mytestwrite = "No" Then
DoCmd.SetWarnings False
DoCmd.RunSQL "INSERT INTO Addresses_NotUsed SELECT
SunstarAccountsInWebir_SarahTest.* FROM SunstarAccountsInWebir_SarahTest WHERE
(((SunstarAccountsInWebir_SarahTest.external_nmad_id)='" & qs!external_nmad_id &
"'));"
DoCmd.SetWarnings True
End If
End If
End With
End If
qs.MoveNext
Next i
End With
'close reference set
qs.Close
Set qs = Nothing
ss.Close
Set ss = Nothing
End Sub
Ok, based on your stated goal, there are a few errors in your approach.
Here is how I understand your goal based on your opening paragraph:
Loop through each record in table TableA. If the record meets
certain complex criteria, search a second table TableB to see if any
records in TableB contain a matching ID value from this record in
TableA. If a match exists, update a field in TableB, otherwise, export the record from TableA to Excel.
I will describe how the code you have presented is processing your data, and then I will explain how I would approach this problem.
First, as #ScottHoltzman alluded, the DoCmd.TransferSpreadsheet statement that you have in your code will, of course, transfer the entire table to Excel because that is what you told it to do. The 3rd parameter specifies the data to be exported, and you gave it the full table name, so the full table will be exported.
Second, I think you are misunderstanding how looping through the two RecordSets in your code is actually functioning. Your code is doing the following:
Evaluate a record in qs. If it doesn't meet the criteria, move to the next qs record and repeat step 1.
If the record in qs does meet the criteria, evaluate a record in ss against this record in qs.
If they match, update ss and move to the next ss record, go to step 2, remembering that qs is still pointing at the same record and has not moved.
If they do not match, transfer the entire table to Excel, now move to the next ss record, go to step 2, again remembering that qs is still pointing at the same record and has not moved.
Once all records in ss have been processed through steps 2, 3 & 4, move to the next qs record and go to step 1
I would expect your code to export the table to Excel over and over again many times.
I would also expect your code to get an error as soon as you begin to process the 2nd qs record that moves on to step 2 because after having processed steps 2, 3 & 4 for the first qs record that met your criteria, the ss RecordSet will be pointing at EOF, and you don't have any code to move the pointer back to the first record in ss.
Anyway, since you have a complex criteria for determining if a record is exported or not, I would recommend adding a single True/False field to TableA called ToExport. Now, at the beginning of your code, you would set ToExport = False for all records in TableA. Then, your code would work to evaluate each record in TableA to determine if the record should be exported. If it should, you update ToExport to be True. Once you have looped through the entire table, only the records needing exported will be marked as ToExport = True. Now, you export just the True records to Excel, thereby achieving your desired result.
Here is some code that should achieve this goal in an efficient manner. This code tries to use the tables and criteria from your original source. It also replaces your With blocks and For loops with more useful Do loops, taking advantage of built-in RecordSet looping and EOF checking.
Public Sub EditFinalOutput2()
Dim db As DAO.Database
Dim qs As DAO.Recordset
Dim ss As DAO.Recordset
Dim strSQL As String
Set db = CurrentDb()
strSQL = "UPDATE [SunstarAccountsInWebir_SarahTest] SET ToExport = False;"
db.Execute strSQL
Set qs = db.OpenRecordset("SunstarAccountsInWebir_SarahTest", dbOpenDynaset)
Do While Not qs.EOF
If (IsNull(qs("nmad_address_1")) Or (qs("nmad_address_1") = qs("nmad_city")) Or (qs("nmad_address_1") = qs("Webir_Country")) And IsNull(qs("nmad_address_2")) Or (qs("nmad_address_2") = qs("nmad_city")) Or (qs("nmad_address_2") = qs("Webir_Country")) And IsNull(qs("nmad_address_3")) Or (qs("nmad_address_3") = qs("nmad_city")) Or (qs("nmad_address_3") = qs("Webir_Country"))) Then
MsgBox "This was an invalid address"
Else
strSQL = "SELECT * FROM [1042s_FinalOutput_7] WHERE Right([IRSfileFormatKey], 10) = """ & qs("external_nmad_id") & """;"
Set ss = db.OpenRecordset(strSQL, dbOpenDynaset)
If ss.BOF Then
qs.Edit
qs("ToExport") = True
qs.Update
Else
Do While Not ss.EOF
ss.Edit
ss("box13_Address") = "Test"
ss.Update
ss.MoveNext
Loop
End If
ss.Close
End If
qs.MoveNext
Loop
qs.Close
strSQL = "SELECT * FROM [SunstarAccountsInWebir_SarahTest] WHERE ToExport = True;"
DoCmd.TransferSpreadsheet acExport, 10, strSQL, "\\DTCHYB-MNMH001\C_WBGCTS_Users\U658984\My Documents\pre processor\PreProcessor7\ToBeReviewed\AddressesNotActiveThisYear.xlsx", False
Set qs = Nothing
Set ss = Nothing
db.Close
Set db = Nothing
End Sub
I hope this helps you better achieve your goal.
Create a query like this, and execute it, and return dim rst as Recordset
NOTE: I have changed the AND-s to OR-s as that is what I think you want...
Select qs.*
From
(Select *
From SunstarAccountsInWebir_SarahTest
Where Not
(
(IsNull(nmad_address_1)
Or (nmad_address_1 = nmad_city)
Or (nmad_address_1 = Webir_Country)
OR IsNull(nmad_address_2)
Or (nmad_address_2 = nmad_city)
Or (nmad_address_2 = Webir_Country)
OR IsNull(nmad_address_3)
Or (nmad_address_3 = nmad_city)
Or (nmad_address_3 = Webir_Country)
)
) as qs
Left Join
(Select *
,Right(ss.Fields("IRSfileFormatKey"), 10) as ssKey
From 1042s_FinalOutput_7
) as ss
On qs.external_nmad_id = ss.ssKey
Where ssKey is NULL
Then output the rst --(taken from https://support.microsoft.com/en-us/help/246335/how-to-transfer-data-from-an-ado-recordset-to-excel-with-automation )
' Copy field names to the first row of the worksheet
fldCount = rst.Fields.Count
For iCol = 1 To fldCount
xlWs.Cells(1, iCol).Value = rst.Fields(iCol - 1).Name
Next
' Copy the recordset to the worksheet, starting in cell A2
xlWs.Cells(2, 1).CopyFromRecordset rst
'Note: CopyFromRecordset will fail if the recordset
'contains an OLE object field or array data such
'as hierarchical recordsets
I have Access data I'm trying to export to a Word table. The table has 3 columns, the first row and first column are all headers.
I'm trying to loop through the recordset and populate columns 2 & 3 with data. I'm able to start at row 2 and populate columns 2 and 3, but I cannot figure out how to move to the next row.
iTbl = 1
irow = 2
iCol = 1
Do Until recSet2.EOF
If irow > wDoc.Tables(iTbl).Rows.Count Then
wDoc.Tables(iTbl).Rows.Add
End If
For Each fld In recSet2.Fields
On Error Resume Next
iCol = iCol + 1
wDoc.Tables(iTbl).Cell(irow, iCol).Range.Text = Nz(fld.Value)
Next fld
recSet2.MoveNext
irow = irow + 1
iCol = 1
Loop
The best way to create a table in Word, especially one with a lot of data, is to first write the data into a character-delimited string format. Assign the string to a Range in Word, then use the ConvertToTable method to turn it into a table. That will save a lot of trouble with manipulating the object model and is the most efficient approach (fastest in execution).
The following code demonstrates this principle. The procedure Test creates a new instance of Word, creates a new document in the Word application then assigns the character-delimited string to the document content. This is then turned into a table. If you need to format that table, use the tbl object to do so. The way this code is written requires a reference to the Word object library (early binding). Note that it's also possible to use late-binding - you'll find loads of examples for that.
The second procedure, concatData is called in Test to create the character delimited string. It uses a Tab character as the field separator and a carriage return as the record separator. Word will accept pretty much anything as the field separator; the record separator must be a carriage return (ANSI 13).
Sub Test()
Dim wd As Word.Application
Dim doc As Word.Document
Dim rng As Word.Range
Dim tbl As Word.Table
Set wd = New Word.Application
wd.Visible = True
Set doc = wd.Documents.Add
Set rng = doc.Content
rng.Text = concatData()
Set tbl = rng.ConvertToTable
End Sub
Public Function concatData() As String
Dim retVal As String
Dim rsHeader As Long, rsCounter As Long
Dim rs As Recordset
Set rs = CurrentDb.OpenRecordset("nameOfRecordset", dbOpenDynaset)
'Get headers
For rsHeader = 0 To rs.Fields.Count - 1
retVal = retVal & rs.Fields(rsHeader).Name & vbTab
Next
'Replace last TAb with a carriage return
retVal = Left(retVal, Len(retVal) - 1) & vbCr
Do While Not rs.EOF
'Get all records
For rsCounter = 0 To rs.Fields.Count - 1
retVal = retVal & rs.Fields(rsCounter).Value & vbTab
Next
retVal = Left(retVal, Len(retVal) - 1) & vbCr
rs.MoveNext
Loop
concatData = retVal
End Function
Thanks for all the help guys. I managed to figure it out and works very well. It wouldn't move down to the next row and was attempting to write data to column(4) which doesn't exist, then throwing an error. Here is the code I used:
iTbl = 1
iRow = 2
iCol = 1
For Each fld In recSet2.Fields
iCol = iCol + 1
If iCol < 4 Then
wDoc.Tables(iTbl).Cell(iRow, iCol).Range.Text = Nz(fld.value)
Else
If iCol > 3 Then
iCol = iCol - 2
iRow = iRow + 1
wDoc.Tables(iTbl).Cell(iRow, iCol).Range.Text = Nz(fld.value)
End If
End If
Next fld
The task: Create a report for Part Numbers that shows several types (On Hand, On Order, etc) in date buckets with each type totaled for the specific range.
For example:
Item 1 => (could be over 2000)
2/5/2017 2/19/2017 2/28/2017 (30 weeks)
On Hand 20 42 33
On Order 0 5 4
Each item is shown on it's own page with related metadata about the item. Each date bucket is based on a user-entered start date with a calculation running against the data set to determine what goes in which bucket and what the totals are.
I have this report fully working for one item. User types one item, selects a date, and the report is created using the following:
Inventory Meta general information and description of the item
Inventory Detail gets all the detailed information
Inventory Totals gets totals for each Types
GetInventory() VBA sets up the buckets and populates the totals
Using a query to get the date buckets would perhaps be easier to get the data into the report. Creating a query with 210 calculated columns (7 types, 30 weeks) wasn't a reasonable approach.
Naturally, selecting one item at a time is not what's wanted.
I have a select box that gets whatever Part Numbers are selected and creates a query on the fly for the Inventory Meta (main report). I have similar code working that runs with the Inventory Totals (sub report) to create a query on the fly for that.
But, as with the Inventory Totals query, each date is a unique value and is it's own row. What I need to be able to do is run the code to build the buckets for each item selected.
I'm stuck.
I have created an array of item numbers (whatever was selected). I can see what's in the array.
What I can't seem to figure out is how to feed each to the code that runs the date comparisons and calculations so that I get a full set of data for each Part Number.
With one number it was easy.... "this one"
vItem = [Forms]![fOptions]![ItemNumber]
Set db = CurrentDb
strSelect = "Select * FROM qInventoryTotals WHERE qInventoryTotals.ItemNumber = [this_one]"
Set qdf = db.CreateQueryDef(vbNullString, strSelect)
qdf.Parameters("this_one").Value = vItem
Set inv = qdf.OpenRecordset
The closest I've come is getting the report to show the same set of data for all part numbers. I suspect there is some small but critical thing, like where a particular loop starts or a variable I've missed or something.
The result of the following is a message box that repeats the same total for each of the part numbers.
Private Sub CreateOne_Click()
On Error GoTo Err_cmdOpenQuery_Click
'----------- Selection box check for dates -------------
If IsNull(Forms!fFish1!Week1) Then
MsgBox "A Sunday date must be selected", , "Please select a date"
ElseIf Weekday(Forms!fFish1!Week1) = 1 Then
'MsgBox "That is Sunday"
Forms!fFish1!Week1 = Forms!fFish1!Week1
Else
MsgBox "Starting Week needs to be a Sunday date" _
, , "Sorry, that's not Sunday"
' clears the 'not Sunday' selection
Forms!fFish1!Week1 = ""
Exit Sub
End If
'-------------------------------------------------
' Declarations =====================================
Dim db As DAO.Database
Dim iMeta As DAO.QueryDef
Dim i As Integer
Dim strSQL As String
Dim strWhere As String
Dim strIN As String
Dim flgSelectAll As Boolean
Dim varItem As Variant
Dim strSlect As String
Dim vItem As Variant
' Setup -------------------------------------
Set db = CurrentDb()
strSQL = "SELECT * FROM qInventoryTotals2"
'----------------------------------------------------------------------
' Get whatever is selected and loop through the selections
' This defines which numbers are in the list
'----------------------------------------------------------------------
For i = 0 To Forms!fFish1.box4.ListCount - 1
If Forms!fFish1.box4.Selected(i) Then
If Forms!fFish1.box4.Column(0, i) = "All" Then
flgSelectAll = True
End If
strIN = strIN & "'" & Forms!fFish1.box4.Column(0, i) & "',"
End If
Next i
'Create the WHERE string, and strip off the last comma of the IN string
strWhere = " WHERE [ItemNumber] in " & _
"(" & Left(strIN, Len(strIN) - 1) & ")"
'If "All" was selected in the listbox, don't add the WHERE condition
If Not flgSelectAll Then
strSQL = strSQL & strWhere
End If
'-------------------------------------------------------
' Create a query that has all the selected item numbers
db.QueryDefs.Delete "qInventoryTotals3"
Set iMeta = db.CreateQueryDef("qInventoryTotals3", strSQL)
Set inv = iMeta.OpenRecordset
'==========================================================================
' Create an array to pull out each of the Item numbers one at a time
Dim Count As Integer, r As Integer
Count = 0
For i = 0 To Forms!fFish1.box4.ListCount - 1
If Forms!fFish1.box4.Selected(i) Then
vItem = Forms!fFish1.box4.Column(0, i)
'vItemFilter = Forms!fFish1.box4.Column(0, i)
'MsgBox (vItem), , "one by one"
Count = Count + 1
End If
Next i
''MsgBox (Count), , "count how many items are in the set"
' Get the count for how many items are in the currently selected list
' Displays one item at a time -
' Set up the array ------------------------------
'------------------------------------------------
ReDim vItem(Count)
r = 0
For i = 0 To Forms!fFish1.box4.ListCount - 1
If Forms!fFish1.box4.Selected(i) Then
vItem(r) = Forms!fFish1.box4.Column(0, i)
r = r + 1
End If
Next i
'Check the values stored in array
''For i = 0 To Count - 1
''MsgBox vItem(i), , "show all values from the array"
''Next
' have all values from the array. Each in it's own message box
'===============================================================================
' Set up the item numbers ---------------------------
Dim part As Variant
part = vItem
With vItem
For i = LBound(vItem) To UBound(vItem) - 1
MsgBox ("There are" & " " & (vItem(i)) & " " & "fishies"), , "Whatcha' got now?"
' cycles through each number
' Past Due ============================================
Dim tPOPast As Double
Dim tBCPast As Double
Dim tBPast As Double
Dim tEPast As Double
If inv!ItemNumber = part(i) And inv.Fields("RequiredDate") < Forms!fFish1!Week1 Then
'displays the first part number with it's value, then the remaining numbers with no value
' If inv.Fields("RequiredDate") < Forms!fFish1!Week1 Then
'displays each of the part numbers with the same value
tBPast = inv.Fields("TotalOnHand")
tPOPast = tPOPast + inv.Fields("SumOfSupply")
tBCPast = tBCPast + inv.Fields("SumOfDemand")
' Calculate ending inventory for the week ===================
tEPast = tBPast + tPOPast + tBCPast
' Show something for testing ==============================
MsgBox (tBPast & " " & part(i)), , "show Me the money" ' displays same total for each part number
End If
'end this condition, next condition follows
'----------------- do it again -------------------------------
Next
' Finished with the weekly buckets =====================================
End With
'=========================================================================
'-------------------- error management for the selection box ------------------
Exit_cmdOpenQuery_Click:
Exit Sub
Err_cmdOpenQuery_Click:
If Err.Number = 5 Then
MsgBox "Pick one, or more, item numbers from the list" _
, , "Gotta pick something!"
Resume Exit_cmdOpenQuery_Click
Else
'Write out the error and exit the sub
MsgBox Err.Description
Resume Exit_cmdOpenQuery_Click
End If
'---------------------------------------------------------------------------
End Sub
The solution I found was to set variables for the values from the array and use them to dynamically update a table. From that, I created a query to sum the values and used that as the basis for the report. The key was GetRows()
Get the unique items and read the the rows of data into the first array
Dim rNum As Integer
rNum = myItems.RecordCount
Dim varItem As Variant
Dim intRi As Integer 'rows of unique items
Dim intCi As Integer 'columns of unique items
Dim intNCi As Integer
Dim intRCi As Integer
varItem = myItems.GetRows(rNum)
intNRi = UBound(varItem, 2) + 1
intNCi = UBound(varItem, 1) + 1
For intRi = 0 To intNRi - 1
For intCi = 0 To intNCi - 1
vItem = varItem(intCi, intRi)
Use vItem to dynamically create a new recordset to set up the weekly buckets
strSelect = "Select * FROM qInventoryTotals2 WHERE qInventoryTotals2.ItemNumber = [this_one]"
Set qdf = db.CreateQueryDef(vbNullString, strSelect)
qdf.Parameters("this_one").Value = vItem
Set inv = qdf.OpenRecordset
Count the records, create a second array
Dim invNum As Integer
invNum = inv.RecordCount
Dim varRec As Variant
Dim intR As Integer
Dim intC As Integer
Dim intNC As Integer
Dim intRC As Integer
Dim cItem As String
Dim cRequired As Date
Dim cPO As Double
Dim cBC As Double
Dim cOnHand As Double
varRec = inv.GetRows(invNum)
intNR = UBound(varRec, 2) + 1
intNC = UBound(varRec, 1) + 1
For intR = 0 To intNR - 1
For intC = 0 To intNC - 1
cItem = varRec(0, intR)
cRequired = varRec(1, intR)
cOnHand = varRec(2, intR)
cPO = varRec(3, intR)
cBC = varRec(4, intR)
cSO = varRec(5, intR)
cPD = varRec(6, intR)
cIN = varRec(7, intR)
cJT = varRec(8, intR)
cWO = varRec(9, intR)
'------------- finish getting inventory columns --------------------
Next intC
And then set up the buckets for each week
If cRequired < Week1 Then
recOut.AddNew
recOut.Fields("ItemNumber") = cItem
recOut.Fields("tB") = cOnHand
recOut.Fields("tPO") = cPO
recOut.Fields("tBC") = cBC
recOut.Fields("tSO") = cSO
recOut.Fields("tPD") = cPD
recOut.Fields("tIN") = cIN
recOut.Fields("tJT") = cJT
recOut.Fields("tWO") = cWO
recOut.Fields("tE") = cOnHand + cPO + cBC + cSO + cPD + cIN + cJT + cWO
recOut.Fields("RequiredDate") = cRequired
recOut.Fields("GroupDate") = Week1
recOut.Update
' tE0 = cOnHand + cPO + cBC + cSO + cPD + cIN + cJT + cWO
Dim tryme As Double
tryme = DLookup("teMe", "qBuckets", "GroupDate = Week1")
tE0 = tryme
End If
Doing data analysis for a lab and I have a table of samples that failed and all the criteria they could've failed on. Trying to add a field with a string listing which criteria each sample failed on.
I just learned VBA 2 weeks ago so I don't really know what I'm doing. I used recordset to turn my table into an array, then looped through each record to see if each criteria has failed and add it to a new failure array if it has. Then I print the failure array in an ugly concatenated string. There are less than 100 records but it's still very slow and sometimes crashes Access. Here's my code:
Option Compare Database
Option Explicit
Dim arrFails() As Variant
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim HType As Integer
Dim S As Integer
Public Sub MakeArrs()
On Error GoTo ErrorHandler
Set db = CurrentDb
'Set rs = db.OpenRecordset("S" & HType & "RptSimple")
Set rs = db.OpenRecordset("S31RptSimple")
rs.MoveLast
rs.MoveFirst
S = rs.RecordCount - 1
Debug.Print S
Dim arrRpt() As Variant
arrRpt = rs.GetRows(S + 1)
Debug.Print arrRpt(0, 0)
'This line creates an array arrFails with sample runs as rows, and 9 columns. Each column is a failure criteria.
ReDim arrFails(0 To S, 0 To 8) As Variant
Dim i As Long
Let i = 0
Dim index As Long
'This For loop starts at the first record in arrRpt and goes across the row with an If loop for each of the failure criteria.
'If the sample failed for that criteria, it populates the new arrFails array with the name of the criteria.
'If the sample passed, that spot on the array stays null.
'At the end of one loop, we have a row that ONLY has values for the criteria that failed.
For index = 0 To S
If arrRpt(2, i) < 0.85 Or IsNull(arrRpt(2, i)) = True Then
arrFails(i, 0) = "Correlation, "
End If
If arrRpt(3, i) > -0.4 Or arrRpt(3, i) < -2 Or IsNull(arrRpt(3, i)) = True Then
arrFails(i, 1) = "Slope, "
End If
If arrRpt(4, i) < 0.5 Or arrRpt(4, i) > 100 Or IsNull(arrRpt(4, i)) = True Then
arrFails(i, 2) = "Slope_Ratio, "
End If
If arrRpt(5, i) < 2 Or IsNull(arrRpt(5, i)) = True Then
arrFails(i, 3) = "Valid_Points, "
End If
If IsNull(arrRpt(6, i)) = False Then
arrFails(i, 4) = "Fail_Code, "
End If
If arrRpt(7, i) < 1.5 Or arrRpt(7, i) > 10 Or IsNull(arrRpt(7, i)) = True Then
arrFails(i, 5) = "DilutionRatio1, "
End If
If arrRpt(8, i) < 1.5 Or arrRpt(8, i) > 10 Or IsNull(arrRpt(8, i)) = True Then
arrFails(i, 6) = "DilutionRatio2, "
End If
arrFails(i, 8) = arrRpt(0, i)
i = i + 1
Next
rs.Close
'This is error handling code, so if something goes wrong it'll gracefully exit the code instead of getting some poor user stuck in debug hell.
ExitSub:
Exit Sub
ErrorHandler:
MsgBox "There's been an error."
Resume ExitSub
Set rs = Nothing
Set db = Nothing
End Sub
Public Function FailList2(HPVType, UIDFieldname)
HType = HPVType
Call MakeArrs
Dim x As Variant
x = 0
Do While x < S + 1
If UIDFieldname = arrFails(x, 8) Then
FailList2 = arrFails(x, 1) & arrFails(x, 0) & arrFails(x, 2) & arrFails(x, 3) & arrFails(x, 4) & arrFails(x, 5) & arrFails(x, 6)
Exit Do
End If
x = x + 1
Loop
End Function
Help a newbie out? There must be a more efficient way to do this. I tried turning echo off until the end of the FailList2 function but it didn't help. Note that I need to keep 'Htype' in the function. I'm just running this on one table right now, but when I fix it I have 8 more tables to run it on, hence the rs code I commented out at the beginning.
I have that function as a field in a query
Uh-oh. There is the problem. Just open the Immediate window, open your query and watch the Debug.Print statements roll in. The function will be executed over and over again.
You need to execute the function once, write the results not into an array (arrFails), but into a table instead. Use Recordset.AddNew to add records.
Then use that table as input for your query.
In our code, we have a few Excel objects and a few subs and functions.
We edited a few things and now, for some reason, our objects aren't working inside the sub, the give a "Object Required" error.
We don't know what to do anymore, so any help would be greatly appreciated!
Note: We added the entire code in case there would be questions about declarations and that...
Sub:
Sub birthday (formatDate, i, intRow)
'Take date from database, separate it to days & months
Dim month, day, name
eventDate = Split(formatDate,"/")
month = eventDate(0)
day = eventDate(1)
'Get name of event out of database (one column to the right, from date of event)
name = "netch"
'Get value of row which is used to write events in the specific month
Dim k, row, c
k = 1
wscript.echo objXLCal.Cells(k, 2).Value
Do Until objXLCal.Cells(k, 2).Value = monthRet(month)
k = k + 1
Loop
'k will be used to find the day column, while row is where the events of that months are written
row = k + 3
c = 1
'Get value of column
Do Until objXLCal.Cells(k,c).Value = eval(day)
c = c + 1
Loop
'Insert name of event into place
If Asc(name) = 63 Then
objXLCal.Cells(row,c).Value = StrReverse(name)
Else
objXLCal.Cells(row,c).Value = name
End If
End Sub
Rest of code:
main("C:\Users\liatte\Desktop\hotFolder\Input")
Function main(argFilePath)
Dim templatePath
'-----------------------------------------------------------------------------
'Path to calendar template
templatePath = "C:\Users\liatte\Desktop\Aviv Omer Neta\Birthdays\Calendar1.xlsx"
'-----------------------------------------------------------------------------
'creates the msxml object
'Set xmlDoc = CreateObject("Msxml2.DOMDocument.6.0")
'Dim retVal
'load the xml data of the script
'retVal=xmlDoc.load(argFilePath)
Dim fso, folder, sFolder, inputFolder, xmlDataPath, curNode
'get input folder
'Set curNode=xmlDoc.selectSingleNode("//ScriptXmlData/inputFilePath")
'inputFolder=CSTR(curNode.text)
'location of input folder
'sFolder=inputFolder
sFolder=argFilePath
'creating file getting object
Set fso = CreateObject("Scripting.FileSystemObject")
Set folder = fso.GetFolder(sFolder)
'loop that runs on files in input - RUNS JUST ONCE
'For each folderIdx In folder.files
'Creating object for user excel
Set objXLBirth = CreateObject("Excel.Application")
Set objWorkbookBirth = objXLBirth.Workbooks.Open("C:\Users\liatte\Desktop\hotFolder\Input\Birthdays.xlsx")
'Creating object for calendar template excel
Set objXLCal = CreateObject("Excel.Application")
objXLCal.DisplayAlerts = false
Dim picStr, srcMonth, k, i, intRow, formatDate, txtStr
'Beginning reading from line 2, skipping header
intRow = 2
'loop for each person in user excel
Do Until objXLBirth.Cells(intRow,1).Value = ""
i=2
'Opening the template as new in each round of loop
Set objWorkbookCal = objXLCal.Workbooks.Open(templatePath)
'Cover pic
If Not objXLBirth.Cells(intRow, i).Value = "" Then
objXLCal.Cells(2, 49).Value = objXLBirth.Cells(intRow, i).Value
End If
'Month pic inserter
For i=3 To 14
If Not objXLBirth.Cells(intRow,i).Value = "" Then
picStr = objXLBirth.Cells(1,i).Value
srcMonth = monthRet(Mid(picStr,4))
k=1
Do Until objXLCal.Cells(k, 2).Value = srcMonth
k=k+1
Loop
objXLCal.Cells(k, 47).Value = objXLBirth.Cells(intRow,i).Value
End If
Next
i=15
'Cover text inserter
If Not objXLBirth.Cells(intRow, i).Value = "" Then
objXLCal.Cells(2, 50).Value = objXLBirth.Cells(intRow, i).Value
End If
'Month text inserter
For i = 16 To 27
If Not objXLBirth.Cells(intRow,i).Value = "" Then
txtStr = objXLBirth.Cells(1,i).Value
srcMonth = monthRet(Mid(txtStr,5))
k=1
Do Until objXLCal.Cells(k, 2).Value = srcMonth
k=k+1
Loop
If Asc(objXLBirth.Cells(intRow, i).Value)=63 Then
objXLCal.Cells(k, 48).Value = StrReverse(objXLBirth.Cells(intRow, i).Value)
Else
objXLCal.Cells(k, 48).Value = objXLBirth.Cells(intRow, i).Value
End If
End If
Next
i=28
'Birthday inserter
Do Until objXLBirth.Cells(intRow,i).Value = ""
formatdate=FormatDateTime(objXLBirth.Cells(intRow,i),2)
Call birthday (formatdate,i,intRow)
i=i+2
Loop
'saving changed calendar
objXLCal.ActiveWorkBook.SaveAs "C:\Users\liatte\Desktop\Aviv Omer Neta\Birthdays\Calendar_" & objXLBirth.Cells(intRow, 1).Value & ".txt", 42
intRow = intRow+1
Loop
'moving file to Success
'fso.MoveFile inputFolder, "C:\Users\liatte\Desktop\Success\"
'Next
objXLBirth.Quit
objXLCal.Quit
End Function
Another function:
Function monthRet(month)
Select Case month
Case "1"
monthRet="January"
Case "2"
monthRet="February"
Case "3"
monthRet="March"
Case "4"
monthRet="April"
Case "5"
monthRet="May"
Case "6"
monthRet="June"
Case "7"
monthRet="July"
Case "8"
monthRet="August"
Case "9"
monthRet="September"
Case "10"
monthRet="October"
Case "11"
monthRet="November"
Case "12"
monthRet="December"
End Select
End Function
Thank you very much!
Given a code layout like:
Sub birthday (formatDate, i, intRow)
...
wscript.echo objXLCal.Cells(k, 2).Value
...
End Sub
Function main(argFilePath)
...
Set objXLCal = CreateObject("Excel.Application")
...
End Function
main "C:\Users\liatte\Desktop\hotFolder\Input"
an "Object required" error for the WScript.Echo line is to be expected (the local variable objXLCal initialized in main isn't the same as the (therefore) uninitialized local variable objXLCal in birthday).
The correct solution would be to start with "Option Explicit" and follow the principles of decent procedural programming in VBScript, but the disgusting hack of Diming variables like objXLCal at the top/global level won't lower the quality of the published code.