I have retrieve all data from the internet into a 2 dimension array, I know how to use vba recordset and by filter and update using loop. Below is part of the code in vba.
the difficult problem is here:
Using cmd As New MySqlCommand("INSERT INTO `calls` (`CID`, `ctype`) VALUES (#CID, #ctype)", cnn)
This make me could not use any loop through the array and update accordingly.
cmd.Parameters.Add ('#CID').value = arrValue(i,j)
I hope this could be done in kind of below:
for x = 0 to ubound(arrValue,0)
for y = 0 to ubound(arrValue,1)
.fields(arrHeader(y) = arrValue(x,y)
next y
next x
say i, the n-th to be updated, j = the value of the header
extract of vba:
With rs
'Worksheets(strWsName).Activate
'iLastRow = Worksheets(strWsName).Cells(65535, 1).End(xlUp).row 'column B, column 2
For i = 0 To UBound(arrValue, 1)
Debug.Print "Updating " & i + 1 & " of " & UBound(arrValue, 1) + 1 & " news ..." ' of " & strCodeAB & " ..."
'Start looping the news row
strNewsID = arrValue(i, 1) 'Worksheets(strWsName).Range(ColRefNewsID & i).Value
If strNewsID = "" Then
Debug.Print i - 1 & " news is updated to database"
i = UBound(arrValue, 1)
Else
strFilter = "fID='" & strNewsID & "'"
rs.Filter = strFilter
If rs.EOF Then
.AddNew 'create a new record
'add values to each field in the record
For j = 0 To UBound(arrTitle_ALL)
'20140814
strFieldValue = .Fields(arrAccessField(j))
strNewValue = arrValue(i, j)
If IsNull(strFieldValue) And strNewValue = "" Then
Else
.Fields(arrAccessField(j)) = arrValue(i, j) 'Worksheets(strWsName).Cells(i, j + 1).Value
End If
Next j
On Error Resume Next '***************
.Update 'stores the new record
On Error GoTo 0
iCounterNewsAdded = iCounterNewsAdded + 1
glcounterNewsAdded = iCounterNewsAdded
Else
It seems that the below post similar to my request but I don't know how to do so.
[reference]passing an Array as a Parameter to be used in a SQL Query using the "IN" Command
The post you mention (Using the IN() command) works in your WHERE clause but it is not valid for values. MySQL has no way to pass an array so you need to loop through your array and run multiple INSERT statements:
for y = 0 to ubound(arrValue,1)
cmd.Parameters.AddWithValue(#CID,arrValue(0,y))
cmd.Parameters.AddWithValue(#ctype,arrValue(1,y))
cmd.ExecuteNonQuery
next y
Related
I have two txt boxes and two combo boxes on a form. There is also a subform linked to the temptable that I want to have rebuilt/filter each time one of the controls is changed (using after update on each control to trigger the following sub)
I receive Run-time error '91: Object variable or with block variable not set on line Items(i) = Thing
I am not sure using " (i) " works with MS Access 365 or I am dimensioning incorrectly?
Thank you.
Private Sub Lookupstuff()
Dim i As Integer
Dim Items(1 To 4) As Object
sql = "DELETE * FROM tblTemp"
CurrentDb.Execute sql
i = 0
FilterArray = Array(Me.txtNew, Me.cmbS, Me.cmbP, Me.txtSl)
For Each Thing In FilterArray
If Not IsNull(Thing) Then
i = i + 1
Items(i) = Thing <--Error is here. Items(i) is empty.
End If
Next
If i = 0 Then
Forms!frmNew.Requery
Forms!frmNew.Refresh
End If
If i = 1 Then
Filter = Items1
End If
If i = 2 Then
Filter = Items1 & " AND " & Items2
End If
If i = 3 Then
Filter = Items1 & " AND " & Items2 & " AND " & Items3
End If
If i = 4 Then
Filter = Items1 & " AND " & Items2 & " AND " & Items3 & " AND " & Items4
End If
sql = "INSERT INTO tblTemp SELECT * FROM tblQ"
If Not IsNull(Filter) Then
sql = sql & " WHERE " & Filter
End If
CurrentDb.Execute sql
Forms!frmNew.Requery
Forms!frmNew.Refresh
End Sub
Since you are assigning a reference to object in the array, you must use Set, i.e.:
Set Items(i) = Thing
Also, presumably each reference to Items1, Items2 etc. should actually be Items(1), Items(2) in order to access the objects referenced at these indices of the array.
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.
Does anyone have a short script in VBscript for transposing a Matrix (given as CSV (comma separated values) file)?
A, 1, 2, 3
B, 7, 5, 6
->
A, B
1, 7
2, 5
3, 6
Many Thanks in advance
Tom
So by creating dynamic arrays and auto-increment their growth in parallel with discovering new columns of the original matrix, you can auto build the new data structure quite quickly.
Const OutputCSV = "C:\op.csv"
Dim dt_start, WriteOutput : dt_start = Now
Dim fso : Set fso = CreateObject("Scripting.FileSystemObject")
Dim file : Set file = fso.OpenTextFile("C:\test.csv", 1, True)
Set WriteOutput = fso.OpenTextFile(OutputCSV, 8, True)
Dim fc : fc = file.ReadAll : file.close : Dim fcArray : fcArray = Split(fc, vbCrLf)
WScript.echo "Before Transpose"
WScript.echo "----------------"
WScript.echo fc
WScript.echo "----------------"
Dim opArray() : ReDim opArray(0)
For Each row In fcArray
Dim tmp: tmp = Split(row, ",")
For ent=0 To UBound(tmp)
If ent > UBound(opArray) Then
ReDim Preserve opArray(UBound(opArray)+1)
opArray(ent) = Trim(tmp(ent))
Else
If Len(opArray(ent)) > 0 Then
opArray(ent) = opArray(ent) & "," & Trim(tmp(ent))
Else
opArray(ent) = Trim(tmp(ent))
End If
End If
Next
Next
Dim dt_end : dt_end = Now
WScript.echo "After Transpose"
WScript.echo "----------------"
WScript.echo Join(opArray, vbCrLf)
WScript.echo "----------------"
WScript.echo "Script Execution Time (sec): " & DateDiff("s", dt_start, dt_end)
WriteOutput.Write Join(opArray, vbCrLf) : WriteOutput.Close
If it's just two lines with an equal number of values, you can read both into arrays using the Split function:
a1 = Split(FileIn.ReadLine, ",")
a2 = Split(FileIn.ReadLine, ",")
Then, iterate the arrays and write each element:
For i = 0 To UBound(a1)
FileOut.WriteLine a1(i) & ", " & a2(i)
Next
I'm assuming you know how to open files for reading and writing?
Edit: It sounds like you may have an unknown number of rows to read. In that case, you can use an array of arrays:
Dim a(255) ' Hold up to 255 rows. Adjust as needed. Or use ReDim Preserve to grow dynamically.
Do Until FileIn.AtEndOfStream
a(i) = Split(FileIn.ReadLine, ",")
i = i + 1
Loop
Then, to write:
For j = 0 To UBound(a(0))
' Concatenate the elements into a single string...
s = ""
For k = 0 To i - 1
s = s & a(k)(j) & ","
Next
' Write the string without the final comma...
FileOut.WriteLine Left(s, Len(s) - 1)
Next
I am using the the program FrontlineSMS and some code written in VBScript to take incoming SMS messages and log them to a CSV file. However, random characters such as percents and numbers are ending up in the CSV file even though they are not in the SMS. Below is an example of what I mean:
I send an SMS with my phone to the modem connected to the computer reading
"07/12/2013 11:29:56 25 Happy Holidays"
The modem then receives the message and passes it on the script, which outputs it to a .CSV file. However when I open the file it reads:
"07%2F12%2F2013 | 11%3A29%3A56 | 25 | Happy | Holidays |
Where each word is in its own cell. I need help in figuring out how to get rid of the extra characters that show up (like "%2F"), my guess is that it has to do with the encryption/decryption of the characters when converting to .CSV but I don't know where to start looking to solve this.
Edit: I found out that it has to do with the ASCII coding. "%2F" is Hex for a slash "/", but I still don't know how to prevent this from happening.
Thanks!
Here is the entire script:
Option Explicit
Dim first, secnd
Dim fso, outFile
Dim strFile, strValues, strLine, strInfo
Dim stamp, num, i, identify
Const ForAppending = 8
'error handling/format
'Settings
identify = WScript.Arguments(1)
CStr(identify)
stamp = MyDate()
CStr(stamp)
strFile = "C:\SMScomm\Log\" &identify &" " &stamp & " log.csv"
'Create the file system object
Set fso = CreateObject("Scripting.FileSystemObject")
'Check whether argument were passed
If WScript.Arguments.Count <> 1 Then
WScript.Echo "No arguments were passed"
End If
strInfo = WScript.Arguments(0)
'Replace(strInfo, "%2C", ",")
'Split the argument from FSMS so it reads normally
strValues = Split(strInfo, "+")
'Open to append
Set outFile = fso.OpenTextFile(strFile, ForAppending, True)
num = UBound(strValues)
If num = 0 then
WScript.Echo "Formatting error"
End If
Do while i < num + 1
strValues(i) = strValues(i) & ","
i = i + 1
Loop
'Write to the .csv
i = 0
Do while i < num + 1
outFile.Write(strValues(i) + " ")
i = i + 1
Loop
outFile.WriteBlankLines(1)
'Close the file
outFile.Close
'Clean up
Set outFile = Nothing
Set fso = Nothing
Function MyDate()
Dim dteCurrent, dteDay, dteMonth, dteYear
dteCurrent = Date()
dteDay = Day(dteCurrent)
dteMonth = Month(dteCurrent)
dteYear = Year(dteCurrent)
MyDate = dteMonth & "-" & dteDay & "-" & dteYear
End Function
It looks like either your script or the modem is converting special characters such as "/" into their Hex format.
Can you post the script that dumps this information into CSV format?
Option Explicit
Dim first, secnd
Dim fso, outFile
Dim strFile, strValues, strLine, strInfo
Dim stamp, num, i, identify
Const ForAppending = 8
'error handling/format
'Settings
identify = WScript.Arguments(1)
CStr(identify)
stamp = MyDate()
CStr(stamp)
strFile = "C:\SMScomm\Log\" &identify &" " &stamp & " log.csv"
'Create the file system object
Set fso = CreateObject("Scripting.FileSystemObject")
'Check whether argument were passed
If WScript.Arguments.Count <> 1 Then
WScript.Echo "No arguments were passed"
End If
strInfo = WScript.Arguments(0)
'Replace(strInfo, "%2C", ",")
'Split the argument from FSMS so it reads normally
strValues = Split(strInfo, "+")
'Open to append
Set outFile = fso.OpenTextFile(strFile, ForAppending, True)
num = UBound(strValues)
If num = 0 then
WScript.Echo "Formatting error"
End If
Do while i < num + 1
strValues(i) = strValues(i) & ","
i = i + 1
Loop
'Write to the .csv
i = 0
Do while i < num + 1
Replace(strValues(i), '%2F', '/')
Replace(strValues(i), '%3A', ':')
outFile.Write(strValues(i) + " ")
i = i + 1
Loop
outFile.WriteBlankLines(1)
'Close the file
outFile.Close
'Clean up
Set outFile = Nothing
Set fso = Nothing
Function MyDate()
Dim dteCurrent, dteDay, dteMonth, dteYear
dteCurrent = Date()
dteDay = Day(dteCurrent)
dteMonth = Month(dteCurrent)
dteYear = Year(dteCurrent)
MyDate = dteMonth & "-" & dteDay & "-" & dteYear
End Function
I am sure there is a more elegant way of doing this but it should solve your problem.
I have made a form that is supposed to get the attendance details for a specific session.
The used elements are:
1- CheckedListBox
2- Combobox :CB_Students
3- Button : Update
My table is Student, which contains Code| First Name| Last Name| Day1| Day2| Day3| Day4| Day5, where the Days are of the type tinyint, referring to Presence or Absence.
I want the user to check the students' names from the CheckedListBox, so I populated the CheckedListBox with the concatenation of First Names and Last Names, each item showing the full name.
Since the CheckedListBox is indirectly created by the database records, I suppose I cannot connect it to the database directly, so I have created a hidden ComboBox with values of Code corresponding to each student shown in the CheckedListBox.
For example, my student table is as below:
Code | F_Name | L_Name
1 | F1 | L1
2 | F2 | L2
The CheckedListBox contains: F1 L1 and F2 L2, and the ComboBox contains 1 and 2.
The day number also is found during the form load and saved as the Public variable Inquiry.
Below is my code for the Update Button:
Dim j, S As Integer
sqlstr = "UPDATE Student SET Day'" & Inquiry & "'=#field1 WHERE Code='" & CB_Students.Items.Item(j) & "'"
DBCmd = New MySql.Data.MySqlClient.MySqlCommand(sqlstr, DBConn)
With DBCmd
For j = 0 To CheckedListBox1.Items.Count - 1
S = CheckedListBox1.GetItemCheckState(j)
If S = 1 Then
.Parameters.AddWithValue("#field1", 1)
ElseIf S = 0 Then
.Parameters.AddWithValue("#field1", 0)
End If
Next j
End With
DBCmd.ExecuteNonQuery()
DBCmd.Dispose()
However, during the execution, I get the error: "Parameter '#field1' has already been defined."
How should I deal with this problem?
Also, is the statement CB_Students.Items.Item(j) used correctly to give my sql string the student code in the ComboBox?
Update 1:
Inquiry is an integer. I also tried the following code:
For j = 0 To CheckedListBox1.Items.Count - 1
S = CheckedListBox1.GetItemCheckState(j)
If S = 1 Then
With DBCmd
.Parameters.AddWithValue("#field1", 1)
.ExecuteNonQuery()
.Dispose()
End With
ElseIf S = 0 Then
With DBCmd
.Parameters.AddWithValue("#field1", 0)
.ExecuteNonQuery()
.Dispose()
End With
End If
Next j
But again, as in my response to Never_Mind, I get the following error at the first DBCmd.ExecuteNonQuery() line: "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''4' =1 WHERE Code='6'' at line 1".
The values seem to be correct. Day number is 4, Field1 is 1 and Code is 6. I don't see what the problem is.
Update 2:
Below is the code for populating the CheckedListBox and the ComboBox, which is in the form load. C_Code is the Class Code, another field in the student table. ClassID is a public integer variable saved in the previous form. This form appears using the .ShowDialog method.
sqlstr = "SELECT * FROM Student WHERE C_Code = '" & ClassID & "'"
DBCmd = New MySql.Data.MySqlClient.MySqlCommand(sqlstr, DBConn)
DBDR = DBCmd.ExecuteReader
While (DBDR.Read())
Std_Name = DBDR("F_Name") & " " & DBDR("L_Name")
CheckedListBox1.Items.Add(Std_Name)
CB_Students.Items.Add(DBDR("Code"))
End While
DBCmd.Dispose()
DBDR.Close()
Also, Code is integer.
Update 3:
I have changed Inquiry's datatype into String and removed the single quotes.
Anyhow, I was thinking maybe I could make different field numbers. Something like this:
Dim j, S, k As Integer
sqlstr = "UPDATE Student SET D" & Inquiry & " =#field" & k & " WHERE Code='" & CB_Students.Items.Item(j) & "' "
DBCmd = New MySql.Data.MySqlClient.MySqlCommand(sqlstr, DBConn)
For j = 0 To CheckedListBox1.Items.Count - 1
For k = 1 To CheckedListBox1.Items.Count
S = CheckedListBox1.GetItemCheckState(j)
If S = 1 Then
With DBCmd
.Parameters.AddWithValue("#field" & k, 1)
.ExecuteNonQuery()
.Dispose()
End With
ElseIf S = 0 Then
With DBCmd
.Parameters.AddWithValue("#field" & k, 0)
.ExecuteNonQuery()
.Dispose()
End With
End If
Next k
Next j
But this doesn't work. I think there should be something wrong with the way I've concatenated #field and k while adding through the command. How can I get it to work?
I would really appreciate the help!
Try this
With DBCmd
For j = 0 To CheckedListBox1.Items.Count - 1
S = CheckedListBox1.GetItemCheckState(j)
If S = 1 Then
.Parameters.AddWithValue("#field1", 1)
.ExecuteNonQuery()
ElseIf S = 0 Then
.Parameters.AddWithValue("#field1", 0)
.ExecuteNonQuery()
End If
Next
End With
Just figured the answer. Since we cannot recreate #field1, we recreate the sql string (the whole thing) by putting it in a loop.
For j = 0 To CheckedListBox1.Items.Count - 1
S = CheckedListBox1.GetItemCheckState(j)
sqlstr = "UPDATE Student SET D" & Inquiry & " =#field1 WHERE Code='" & CB_Students.Items.Item(j) & "' "
DBCmd = New MySql.Data.MySqlClient.MySqlCommand(sqlstr, DBConn)
If S = 1 Then
With DBCmd
.Parameters.AddWithValue("#field1", 1)
.ExecuteNonQuery()
.Dispose()
End With
ElseIf S = 0 Then
With DBCmd
.Parameters.AddWithValue("#field1", 0)
.ExecuteNonQuery()
.Dispose()
End With
End If
Next j
This one was quite tough! But heck, it gets easy when it's solved, eh?
First remove the single quote from the string Inquiry to concatenate the progressive of the field to the word day. BEWARE. I suppose that you have full control over the value of the Inquiry variable and check if it is a valid number before concatenating to the Day word
Second Define your parameter just once outside the loop with a dummy value, then, inside the loop set the value to the state of the current item and execute the query
Third Use a parameter also for the code value
j = 0 ' you should initialize this variable to a valid index inside the items collection of the combo'
sqlstr = "UPDATE Student SET Day" & Inquiry & "=#field1 WHERE Code=#code"
DBCmd = New MySql.Data.MySqlClient.MySqlCommand(sqlstr, DBConn)
With DBCmd
.Parameters.AddWithValue("#field1", 0)
.Parameters.AddWithValue("#code", Convert.ToInt32(CB_Students.Items(j)))
For j = 0 To CheckedListBox1.Items.Count - 1
S = if(CheckedListBox1.GetItemCheckState(j) = CheckState.Checked, 1, 0)
.Parameters("#field1").Value = S
.ExecuteNonQuery()
Next j
End With
This approach is not very efficient because an update command is executed for each day item (checked or not), but to remove this inefficiency will require a full rewrite of this code.
As for the usage of the combobox items, I think you should use the SelectedValue property, but it depends on how you have filled the combo and which value is assigned to the ValueMember property and which datatype is the code database field