Refer to OpenArgs to identify field name - ms-access

I am having some issues trying to use OpenArgs to refer to a field name in a table. I'm fairly new to this so please bear with me.
So I have a Report(CourseCatalog) that has text boxes with course names that it pulls from a table(tblCourses). When you click on a course, it opens a Form which gives you the option to rate the course(frmRate). I use OpenArgs (from the report to the rating form) to make the caption for frmRate. Works fine.
Now i need to take the data (number of stars selected, (intNumStars)), which is defined previously in the code, and put it into a table. That table ("Allratings") has course names (the OpenArgs value) as the column names and I want to put the intNumStars(1-5) into the cells in those columns.
I seem to have some problems referring to the VarArgs to accomplish that. My syntax/logic may be (is probably) wrong, and if anyone knows a better way to accomplish this task, please let me know what you think! Thank you!
Private Sub btnSubmit_Click()
Dim varargs
Dim rst As dao.Recordset
Dim db As dao.Database
Dim fld As dao.Field
varargs = Me.OpenArgs
Set db = CurrentDb()
Set rst = db.OpenRecordset("Allratings")
For Each fld In rst.Fields
If fld.Name = "varargs" Then
rst.AddNew
rst!"varargs" = intNumStars
rst.Update
End If
Next
End Sub

You're doing weird things with strings, and you should get a descriptive compile error when you try to save/compile this.
varargs refers to the variable varargs
"varargs" is a literal string containing the letters v, a, r, etc.
rst!"varargs" should be rst.Fields("varargs") to avoid a compile error, but should actually be rst.Fields(varargs)
If you correct all incorrectly placed quotes, you get:
Private Sub btnSubmit_Click()
Dim varargs As String
Dim rst As dao.Recordset
Dim db As dao.Database
Dim fld As dao.Field
varargs = Me.OpenArgs
Set db = CurrentDb()
Set rst = db.OpenRecordset("Allratings")
For Each fld In rst.Fields
If fld.Name = varargs Then
rst.AddNew
rst.Fields(varargs) = intNumStars
rst.Update
End If
Next
End Sub
This seems valid, if Me.OpenArgs contains only a field name

Eric has shown how to make your code work, but your table design will prove impractical. Adding a new column for each new course is not how you work with databases.
I suggest
tblCourses
+----------+------------+
| CourseID | CourseName |
+----------+------------+
| 1 | foo |
| 2 | bar |
+----------+------------+
tblUsers
+--------+----------+
| UserID | UserName |
+--------+----------+
| 7 | John |
| 8 | Kate |
+--------+----------+
allRatings (this is a Junction table)
+----------+--------+----------+
| CourseID | UserID | NumStars |
+----------+--------+----------+
| 1 | 7 | 1 |
| 1 | 8 | 4 |
| 2 | 7 | 3 |
+----------+--------+----------+
So a new rating is a new record in allRatings, with fixed column names. You can get the CourseID from the passed Course Name with a DLookup call.
And a view with Courses and their ratings as columns is achieved with a Crosstab query.

Related

AWS RDS (MySQL) does not output all column content onto Excel via VBA

I have a AWS RDS instance using MySQL. The login has full admin right.
I am trying to perform a SELECT * FROM table and output all the contents of the table onto an Excel worksheet using VBA. Still kind of new to this so I am starting with the basics.
I am using the chunk of code below:
Sub test()
Dim Server_Name As String, DB_Name As String, User_ID As String, User_Pass As String
Dim Rs As Variant, Cn As Variant
Dim sqlStr As String
Dim includeField As Boolean
Dim tgtCell As Range
Dim col As Long
Server_Name = "arachnophobia.blabla.rds.amazonaws.com"
DB_Name = "Spidey"
User_ID = "Peter"
User_Pass = "Parker"
sqlStr = "SELECT * FROM crime"
Set Cn = CreateObject("ADODB.Connection")
Cn.Open "Driver={MySQL ODBC 8.0 Unicode Driver};" & _
";Server=" & Server_Name & _
";Database=" & DB_Name & _
";Uid=" & User_ID & _
";Pwd=" & User_Pass
Set Rs = CreateObject("ADODB.Recordset")
Set Rs = Cn.Execute(sqlStr)
Set tgtCell = Sheets("Main").Range("A1")
includeField = True
If includeField Then
tgtCell.Offset(1, 0).CopyFromRecordset Rs
For col = 0 To Rs.Fields.Count - 1
Sheets(tgtCell.Parent.Name).Cells(1, col + 1).Value = Rs.Fields(col).Name
Next
Else
tgtCell.CopyFromRecordset Rs
End If
End Sub
The crime table consists of 4 column:
|S/N (PK) |DateID |CrimeID |Reason |
|1 |01/02/19 |32155 |Fun |
|2 |03/02/19 |32255 |Bored |
|3 |06/02/19 |32555 |Meh |
|4 |07/02/19 |32755 |Duh |
|5 |09/02/19 |32855 |Null |
I executed the code above and the output on the Excel sheet is:
|S/N (PK) |DateID |CrimeID |Reason |
|1 | | | |
|2 | | | |
|3 | | | |
|4 | | | |
|5 | | | |
Basically, I am only getting the Field names and the Primary Key values but not anything else.
I tried running the same code on my local server and it works just fine.
Is there anything I did not take into consideration in the code that causes all the missing column information?
Update:
I have done more testing. tgtCell.CopyFromRecordset Rs results in the output above. However, if I try looping through the recordset using this:
xlrow = 1
Do While Not Rs.EOF
xlcol = 1
For col = 1 To Rs.Fields.Count
tgtCell.Offset(xlrow, xlcol - 1).Value = Rs(col - 1).Value
xlcol = xlcol + 1
Next
Rs.movenext
xlrow = xlrow + 1
Loop
I will end up with my desired output. I don't quite understand why this is so? Was I missing some kind of library reference?
what happens if you remove the following code?
For col = 0 To Rs.Fields.Count - 1
Sheets(tgtCell.Parent.Name).Cells(1, col + 1).Value = Rs.Fields(col).Name
Next
To me, your line tgtCell.Offset(1, 0).CopyFromRecordset Rs should just copy the data straight in so I'm wondering if your subsequent code is overwriting/clearing the data.

Select and display all rows belonging to a specific ID

I have
Table student, student_subject and subject_bsit
"student"
-----------------------
|studentID | FullName |
-----------------------
|1234 | John |
|1235 | Michael |
|1236 | Bryce |
"subject_bsit"
-----------------------------------
|subject_id| subject_name |grade |
-----------------------------------
| 1 | Programming | 3 |
| 2 | Networking | 2.5 |
| 3 | Algorithm | 1.75|
| 4 | Physical Educ | 2 |
This is the Junction table to connect the
two now.
"student_subject"
----------------------------
| student_id | subject_id |
----------------------------
| 1235 | 1 |
| 1235 | 2 |
| 1235 | 3 |
| 1234 | 1 |
As you can see the table ID 1235 is michael He has three three subjects, subject_id 1,2 and 3. What I want to do is to display all the subject name and grades of michael in textboxes, not in a datagrid view.
As of now I still have failed to output it to textboxes. This is my sample query
sql = "SELECT subject_name " & _
" FROM student_subject " & _
" INNER JOIN subject_bsit ON subject_bsit.subject_id = student_subject.sub_id" & _
" where student_subject.student_id='" & Txtbox.Text & "'"
The Txtbox.text in the last query is where the user will input the ID number.
This is my code on displaying the data to the textbox. I don't have any idea or approach on how can i loop on the textbox and display it on each textbox.
cmd = New MySqlCommand(sql, myconn)
dr = cmd.ExecuteReader
While dr.Read
TextBox1.Text = dr.Item("subject_name").ToString
TextBox2.Text = dr.Item("subject_name").ToString
End While
This is the sample User Interface of what i am trying to achieve. Thank you so much.
When you read a query's resultset, you use a loop as you know.
While dr.Read
' run this for every row in your resultset
...
End While
The While loop keeps going until you have read all the rows.
You don't have to use a loop. If you wish you can read the rows one at a time, like this
If dr.Read
' just the first row
End If
If dr.Read
' just the second row
End If
If dr.Read
' just the third row
End If
...
From your question I guess you have Textbox1, Textbox2, ... Textbox5 on your form. I also guess you have Grade1, Grade2 ....
To handle both of the subject name and grade, change the first line of your query to
sql = "SELECT subject_name, grade " & _
You can populate those items like this:
If dr.Read
TextBox1.Text = dr.Item("subject_name").ToString
Grade1.Text = dr.Item("grade").ToString
End If
If dr.Read
TextBox2.Text = dr.Item("subject_name").ToString
Grade2.Text = dr.Item("grade").ToString
End If
If dr.Read
TextBox3.Text = dr.Item("subject_name").ToString
Grade3.Text = dr.Item("grade").ToString
End If
' more of these sets of four lines to fill your whole form.
This solves your problem. But you probably notice it is absurdly repetitive. What you really need is an array (actually two arrays) of textboxes. You create, and then fill in, these texboxes in your program. I have not debugged this: that is for you do to.
Dim Subjects As Textbox()
Dim Grades As Textbox()
...
Dim rownumber, Y
rownumber = 0
Y = 200
Dim Subject
Dim Grade
While dr.Read
Subject = New Textbox
Subject.Text = dr.Item("subject_name").ToString
Subject.Width = 200
Subject.Height = 40
Subject.X = 175
Subject.Y = Y
Subjects(rownumber) = Subject
Form.Controls.Add(Subject)
Grade = New Textbox
Grade.Text = dr.Item("grade").ToString
Grade.Width = 50
Grade.Height = 40
Grade.X = 400
Grade.Y = Y
Grades(rownumber) = Grade
Form.Controls.Add(Grade)
rownumber = rownumber + 1
Y = Y + 50
End While
When this runs you will have two columns of controls, one for each subject. But this code is complex, and you have to do all the layout of your form with Something.Y = value and then Y = Y + 50 arithmetic.
That's why grid controls exist. They take care of that kind of thing.
If you are looking to create Textboxes dynamically then you should refer to the #OJones answer
You can simply loop over Me.Controls.OfType(Of TextBox)()
cmd = New MySqlCommand(sql, myconn)
dr = cmd.ExecuteReader
While dr.Read
For Each txt As TextBox In Me.Controls.OfType(Of TextBox)()
txt.Text = dr.Item("subject_name").ToString
Next
End While
Or you can do a similar approach if you need to fill the first subjects name inside the textboxes (if returned subjects are more than textboxes additional subjects will be ignored):
While dr.Read = True
Dim txt As New TextBox = DirectCast(Me.Controls.Find(string.Format("Textbox{0}", cnt ),false).FirstOrDefault(),Textbox);
If Not txt Is Nothing Then txt.Text = dr.Item("subject_name").ToString
cnt += 1
End While
dr.Close()

Access bring me random records from a selected column

I want access give me the the first value from the selected table and column.
But access are given to me random value from the selected table and column.
Question
Have someway to access give to me like this example:
My table is "tblExample" and have 2 columns the header of each is [Column1] and [Column2] inside each header contains this values
+---------+----------+
| Column1 | Column2 |
+---------+----------+
| 5 | mark |
| 3 | stewie |
| 2 | stack |
| 16 | overflow |
+---------+----------+
I want read the first value in loop of the [Column1] from the table "tblExample" and bring to me in sequence.
example value = 5/ after 3 / 2 / 16 ...
But my code is showning this values random like 16/5/3/2
This is my code
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim VarA As String, varFR, varLR
Set db = CurrentDb
Set rs = db.OpenRecordset("tblExample")
If rs.EOF Then
FindRecordCount = 0
Else
rs.MoveLast
FindRecordCount = rs.RecordCount
End If
varLR = FindRecordCount
Dim i As Long
For i = 0 To rs.Fields.Count - 1
rs.MoveFirst
varFR = 1
Do While varFR < varLR
If IsNull(rs.Fields(i).Value) = True Then
GoTo pula1
End If
VarA = rs.Fields(i).Value
Debug.Print VarA
pula1:
rs.MoveNext
varFR = varFR + 1
Loop
Next
Tables without primary key don't have an inbuilt sort order. So the result of a recordset loop can be random.
You can either provide the sort order when opening the recordset
Set rs = db.OpenRecordset("SELECT * FROM tblExample ORDER BY Column1")
(but the result can still be random if Column1 contains duplicate values!)
or if you want the order, in which the records were inserted, add a AutoNumber primary key column to your table.
For readability you should still specify the order -
Set rs = db.OpenRecordset("SELECT * FROM tblExample ORDER BY ID")
you can loop trough like this, after you set the Recordset:
rs.MoveFirst
Do Until rs.EOF
--- Stuff happens
rs.MoveNext
Loop
So its looping through the records like they are in the table. If you want another sequence you can sort the recordset before:
Set rs = db.OpenRecordset("SELECT * FROM tblExample ORDER BY Column1 ASC")

VBA script to pull values within specific HTML classes

I created a VBA script to pull prices from websites by getting the value within an HTML Class.
Please see VBA Script pull data from website for more context.
This works really well however there are some cases where there is only 1 price (no RRP & sale price) and therefore i need to somehow incorporate an if statement to look for the a class name, if that doesn't exist look for another.
For example I have the following spreadsheet:
| A | B | C |
| | Item | Price |
| | bfd/garden-structures/arbours/arbours-sunflower | |
| | bfd/garden-structures/arbours/tatton-corner-arbour-seat | |
| | bsd/garden-storage/wooden-storage/4-x-2-windsor-garden-storage-chest | |
In this example the first 2 work with the code below: (looking int class VariantPrice & NowValue) however the 3rd example doesn't work as the classes VariantPrice & NowValue do not exist, however it does have the class price & SinglePrice.
The code I have used is below:
Sub BuyDeckingDirect()
Dim ie As New InternetExplorer
Dim doc As HTMLDocument
Dim result As IHTMLElement
Dim result2 As IHTMLElement
Dim item As String
Dim lRow As Long
'ie.Visible = True'
lRow = 2
item = Worksheets("BuyDeckingDirect").Range("B" & lRow).Value
MsgBox "Price Dump Started"
Do Until item = ""
ie.navigate "http://www.buydeckingdirect.co.uk/" & item
Do
DoEvents
Loop Until ie.readyState = READYSTATE_COMPLETE
Set doc = ie.document
Set result = doc.querySelector(".VariantPrice")
Set result2 = result.querySelector(".NowValue")
Worksheets("BuyDeckingDirect").Range("C" & lRow).Value = result2.innerText
lRow = lRow + 1
item = Worksheets("BuyDeckingDirect").Range("B" & lRow).Value
Loop
MsgBox "BuyDeckingDirect Price Dump Complete"
End Sub
Any help would be really appreciated!
Thanks
Jess
Combine both classes in the call to querySelector and if result is Nothing, call it again with the alternative class.
Example:
' ...
Set result = doc.querySelector(".VariantPrice .NowValue")
If result Is Nothing Then
Set result = doc.querySelector(".VariantPrice .price")
End If
' You should consider the fact that you can have neither
If result Is Nothing Then
Worksheets(...etc...).Value = "N/A"
Else
Worksheets(...etc...).Value = result.innerText
End If
Of course you can also check for the existence of the NowValue class after setting result like so:
' ...
Set result = doc.querySelector(".VariantPrice")
If IsNull(result.querySelector(".NowValue")) Then
Set result2 = result.querySelector(".price")
Else
Set result2 = result.querySelector(".NowValue")
End If
Personally, I prefer the 1st option but it's up to your use case.

Find Lowest Value in Columns Access 2010

I have a table in Access 2010 that has 3 separate priority fields. I have a sub that looks through the columns, finds the smallest number, and puts it in an Overall Priority field.
Ex.
SubProjNo | GOPri | StrPri | SOPri
--------+-----------+----------+------------------
1234-12-01 | 100 | 7 | 61
1234-12-02 | | 18 | 2
1234-12-03 | 51 | |
ProjNo: 1234-12-00 Overall_Priority:2
I originally had the code under Private Sub Form_Current() but it slowed the program down way too much, so I moved it to an AfterUpdate for the subform that the table is in.
Private Sub MFWorkProjectssubform_AfterUpdate()
Dim MinGOPri As Variant
Dim MinStrPri As Variant
Dim MinSOPri As Variant
MinGOPri = DMin("[GOPri]", "[WorkProjects]", "WorkProjects.PROJNO = Activity.PROJNO")
MinStrPri = DMin("[StrPri]", "[WorkProjects]", "WorkProjects.PROJNO = Activity.PROJNO")
MinSOPri = DMin("[SOPri]", "[WorkProjects]", "WorkProjects.PROJNO = Activity.PROJNO")
Overall_Priority = IIf(((IIf([MinGOPri] < [MinStrPri], [MinGOPri], [MinStrPri])))
< [MinSOPri], ((IIf([MinGOPri] < [MinStrPri], [MinGOPri], [MinStrPri]))), [MinSOPri])
End Sub
The problem is, now, all the columns are cleared and only the largest value is left. Any suggestions for how to get this to work, or how to speed it up if I put it back in Form_Current would be really appreciated.
You don't need all this. For the Overall_Priority textbox use this expression as ControlSource:
=IIf(((IIf([GOPri]<[StrPri],[GOPri],[StrPri])))<[SOPri],((IIf([GOPri]<[StrPri],[GOPri],[StrPri]))),[SOPri])
Edit for Null and reduced:
=IIf(IIf(Nz([GOPri],9999)<Nz([StrPri],9999),Nz([GOPri],9999),Nz([StrPri],9999))<Nz([SOPri],9999),IIf(Nz([GOPri],9999)<Nz([StrPri],9999),Nz([GOPri],9999),Nz([StrPri],9999)),Nz([SOPri],9999))
Use this as a fourth column; name it, say, RowMin.
Then, in the footer, use =Min([RowMin]) as the controlsource for your totals box.
How about using recordsets, is this an option ?
Dim rst As Object
Dim minValue As Integer
Dim fieldCounter As Long
Dim minPriority as Long
minPriority = 9999
Set rst = Me.recordsetclone
With rst
.MoveFirst
While Not .EOF
For fieldCounter = 0 To .Fields.Count-1
if(.Fields(fieldcounter).name = "GOPri" or .Fields(fieldcounter).name = "StrPri" or .Fields(fieldcounter).name = "SOPri" ) then
Debug.print "Now you are checking : " &.Fields(fieldcounter).Name & " with value : " & .Fields(fieldcounter) & " Current minPriority = " & minPriority
If len(Nz(.Fields(fieldCounter),"")) > 0 Then
If .Fields(fieldCounter) < minPriority Then minPriority = .Fields(fieldCounter)
End If
End if
Next
.MoveNext
Wend
End With
Set rst = Nothing
Overall_Priority = minPriority
Maybe you need to adjust the fieldCounter to match your Table structure
The fields are counted from 0 -->.... according to your question.
Fields(0) = SubProjNo
Field(1) = GoPRi
If you want this column to always be a function of the other three fields, then you could create a calculated field to the table. The help documents can show you how; also this article gives directions:
https://support.office.com/en-us/article/Add-a-calculated-field-to-a-table-14a60733-2580-48c2-b402-6de54fafbde3
Generally you would define a new field on the table and set it's formula to the maximum of the other three fields. Then any time the calculated field is referenced it will always give you the maximum of them.
The other option is to just not worry about saving the field at all at the time of data entry and simply create a view that adds a field defined to be the maximum of the three values.