How do I create a loop in Access VBA/SQL to select records and output to Excel? - sql

I need some help on writing some VBA/SQL code into a module in Access, which should do the following:
Loop through Table 'Schools', with just one field (SchoolName).
Use each value in the first table to select records from Table 'ChildData', with several fields about individual children, including which school they attend (SchoolName).
For each school, export the data for its attending children into a separate Excel workbook named for that school.
Can anyone give me any starting points? Please be aware that I know very little about either VBA or SQL and am pretty much starting from scratch, therefore even the most basic explanations would be very helpful!
Thanks,
AVN

I can't see anything in your question that indicates you need anything complicated.
The first step is to create a query that joins the schools and students and when that runs, export to Excel via the menus. If that works, then you need to alter it so that you are outputting one school at a time. The "easy" way to do this would be to add to your query a parameter on School so that each time the query runs, you're displaying only one school.
Now, if you want to automate that, it becomes more complicated because the parameter will get in the way.
So, my suggestion would be to create an unbound form that has a combo box on it that displays the list of schools. You then use a reference to that combo box as criteria for your query, and use that to drive the code behind a command button:
Private Sub cmdOutputSchool_Click()
If IsNull(Me!cmbSchool) Then
MsgBox "You must first choose a school from the dropdown.", _
vbExclamation, "Choose a school"
Else
DoCmd.TransferSpreadsheet acExport, acSpreadsheetTypeExcel9, _
"qrySchoolOutput", CurrentProject.Path & "\" & Me!cmbSchool & ".xls"
End If
End Sub
Now, this doesn't automate the process, but it at least makes it easy to output each report in succession.
If you wanted it to be automated, one easy way to do that would be to bind the form to the Schools table, and bind your query to the display of the School Name on the form. Then you could have the command button walk through the records of the form and output the report for each school:
Private Sub cmdOutputSchool_Click()
With Me.RecordsetClone
.MoveFirst
Do Until .EOF
Me.Bookmark = .Bookmark
DoCmd.TransferSpreadsheet acExport, acSpreadsheetTypeExcel9, _
"qrySchoolOutput", CurrentProject.Path & "\" & Me!SchoolName & ".xls"
.MoveNext
Loop
End With
End Sub
This will output a spreadsheet for each school in the table you've bound to the form.

It would be simpler to create a query which joins the two tables on school, and then use a pivot in excel with school as the page, and use the show pages to get each school on a separate page
If you want to do it the more complicated way then in VBA you need a recordset from the schools table which shows all the schools, and a recordset from the childdata with the required fields, and a parameter against the school field.
Then in VBA you use a do loop control, which proceeds while school.EOF=false and for each school record get the childdata recordset, and put it to Excel
I would tend to do this in Excel VBA and use copyfromrecordset to put the data into Excel -simple enough to create a new sheet for each school and set the sheet name to school. You would need to add the relevant references to the VBA project - DAO or ADO - to be able to connect to the Access data.

As mentioned, you can use "" inside a string to get a quote. Example:
MsgBox "I am an ""Example""."
As for the other ,you don't really need VBA to do this. You can do it with a query:
SELECT ChildData.* INTO [Excel 8.0;HDR=YES;IMEX=2;DATABASE=C:\Example.xls].[MyWorksheet]
FROM ChildData INNER JOIN Schools ON ChildData.SchoolName = Schools.SchoolName;
You can of course do the same thing from VBA if you really want to like so:
CurrentDB.Execute "SELECT ChildData.* INTO [Excel 8.0;HDR=YES;IMEX=2;DATABASE=C:\Example.xls].[MyWorksheet] FROM ChildData INNER JOIN Schools ON ChildData.SchoolName = Schools.SchoolName;"
Still another way would be to create a select query that pulled your data and then use DoCmd.OutputTo:
DoCmd.OutputTo acOutputQuery,"MyQuery",acFormatXLS,"C:\Test.xls"
You can also use DoCmd.TransferSpreadsheet:
DoCmd.TransferSpreadsheet acExport, acSpreadsheetTypeExcel8, "MyQuery", "C:\Test.xls", True

Related

Access VBA: How to filter a mainform by searching a subform in a one-to-many relationship?

I've got a form for entering contacts which contains several subforms, many of which represent many-to-many relationships. I wish for users to be able to search contacts by values in their subforms.
Ironically, I actually have a good, functional solution for searching those subforms which represent many-to-many relationships-- usually, those are the ones that give me the most trouble! The problem is, the solution I'm using uses comboboxes, and therefore allows users to only search by the exact values available in the drop-down. Here's what I mean:
Private Sub cboSourceSearch_AfterUpdate()
Dim strSQL As String
If IsNull(Me.cboSourceSearch) Then
Me.Parent.RecordSource = "people"
Else
strSQL = "SELECT DISTINCTROW people.* FROM people " & "INNER JOIN people_has_sourceref ON " & "people.personID = people_has_sourceref.people_personID " & "WHERE people_has_sourceref.sourceref_sourceID = " & Me.cboSourceSearch & ";"
Me.Parent.RecordSource = strSQL
End If
End Sub
(Adapted from http://allenbrowne.com/ser-28.html)
Essentially this VBA is setting the RecordSource of the main form from the value in the combo-box, whose row source is the "sourceref" table-- the third in the many-to-many relationship. This works great, but what if I want to search by a note left on a contact? The table is called "Notes", and is in a many-to-one relationship with People (contacts). It's a free form block of text for entering notes relating to the contact.
I've got a general idea of how this would work, and theoretically it should be easier than the above method: Just set the RecordSource of the main form based on an SQL query which searches the Notes table with wildcard characters on either side. Yet, I can't put together a solution. This is essentially due to my lack of VBA knowledge, but I'm learning.
After typing this, I realized I did in fact have the VBA and SQL know-how to put together a solution, but I thought I'd post this anyway for posterity.
Private Sub NoteSearch_AfterUpdate()
Dim strSQL As String
If IsNull(Me.NoteSearch) Then
Me.Parent.RecordSource = "people"
Else
strSQL = "SELECT DISTINCTROW people.* FROM people INNER JOIN notes ON people.personID = notes.people_personID WHERE notes.note LIKE '%" & Me.NoteSearch & "%'"
Me.Parent.RecordSource = strSQL
End If
End Sub
Please note: I have explicitly included the % wildcard on either side of the text in the code since I consider that to be a more common use-case than explicitly searching for a note whose exact text matches that in the search field. I suppose you could exclude the wildcard and teach the users how to include it themselves, which may be preferable.

MS Access Issue

Hello Stackoverflow community!
I have run into an issue and I'd love some advice.
I'm working with MS Access and I am trying to append two particular fields from one table to another; however this implementation is in a form and it gets a little complicated... So, I'll explain everything the best that I can
BACKGROUND INFORMATION:
First and fore most, I have two tables; one of which is a linked excel spread sheet from another directory (who is not willing to change any formatting what so ever, so I CANNOT make ANY changes to this file and it is being updated on a daily basis). This excel spreadsheet is very large and contains somewhere around 50 columns
The other table is not anywhere near as large but has around 20 columns and is meant to extract two columns from the excel spreadsheet (the first column and the third column). I'm trying to make a form for this database to be as user-friendly as possible and not many people in my office are familiar with the technicalities of Access queries and programming in VBA.
THE SITUATION:
On my form, the user will enter data into TextBoxA, from there they will click a button; this button will trigger a search through the linked excel spreadsheet for the data that was typed into TextBoxA. It will then copy the data from Field1 (which was the typed data) and Field3 and append these selected fields into the first two fields of the table in my Access Database. All of this is being done through a segment of VBA code
Private Sub CmdCloseForm_Click()
If IsNull(Me.TextBoxA) Or Me.TextBoxA = "" Then
MsgBox ("Field is empty, please try again!")
Else
Dim VendorNum As String
SearchingValue = Me.TextBoxA
Dim SQL As String
SQL = "INSERT INTO tbleRecord (Field1,Field2)" & _
"SELECT * " & _
"FROM tbleLinkedExcel " & _
"WHERE Field1 = '" & SearchingValue & "';"
DoCmd.RunSQL SQL
End If
End Sub
So the biggest issue here is that in Field1, and every time I try to run the code,
I receive an error; which I am assuming it is because of the space (unfortunately I cannot give the ACTUAL variable names out as it is confidential)
ERROR MESSAGE
The INSERT INTO statement contains the following unknown field name: 'FIELD 1'. Make sure you have typed the name correctly, and try the operation again.
The thing is, is that this 'FIELD 1' variable/name is not in my code, but in the linked excel spreadsheet (again, I am not able to change ANYTHING on this spreadsheet).
Thanks guys!

How do you declare a FOR EACH loop in MS Access database relationships in VBA?

I have a database for tracking family passes and member usage at our pool. I have 3 tables, one for family details, one for every individual member, and one for tracking check-ins.
There's a relationship that connects each individual record is appropriate family pass. Each individual is issued a pass that is scanned by a barcode scanner and checks them in using a simple form, I want to write a loop for a button that will check in every member of the family when they show up together, instead of having each individual scan their card.
DoCmd.OpenForm "CheckIn"
Dim person As ?
Dim family As ?
FOR EACH person IN family
Forms!CheckIn![PASSHOLDER] = Me![BARCODE]
Forms!CheckIn![CHECKINTIME] = Now()
DoCmd.GoToRecord , , acNewRec
NEXT
DoCmd.Close acForm, "CheckIn", acSaveYes
Problem is I don't know enough about VBA to figure out how to accomplish this. Do I need to create an array first? How do I prepare my data from my relationships in order to execute my loop?
Assuming the normalized structure below between tables, simply use SQL. Recall forms are just visual representation of tables usually for data management (add, edit, save, delete). Since you know what to add, automate the data entry with backend querying.
Family Details
FamilyID ...
Individual Members
MemberID, FamilyID ...
CheckIns
MemberID, PASSHOLDER, CHECKINTIME ...
So in VBA, if a family checks in press the button and run an append query which after refresh will show as individual records in the CheckIn form. This assumes some Family identifier textbox resides on form to be used in WHERE clause and in this case make it an unbound textbox including bar code since these fields will not be used for user data entry but SQL-automated data entry. Even consider an unbound form apart from actual CheckIn for this particular automation.
Private Sub FamilyCheckinCommand_Click()
If Not IsNull(Forms!CheckIn![FamilyID]) Then
strSQL = "INSERT INTO CheckIn (MemberID, PASSHOLDER, CHECKINTIME)" _
& " SELECT m.MemberID,'" & Forms!CheckIn![BarCode] & "', Now()" _
& " FROM IndividualMembers m" _
& " WHERE m.FamilyID = " & Forms!CheckIn![FamilyID]
' RUNS QUERY
CurrentDb.Execute strSQL, dbFailOnError
' REFRESHES FORM
Me.Form.Requery
Else
Msgbox "Please enter a family!", vbExclamation, "MISSING FAMILY"
End If
End Sub
Can you try like this? It may work :) (without the rest of the code and not knowing what exactly is family it is hard to say):
DoCmd.OpenForm "CheckIn"
Dim person As variant
Dim family As variant
'you should assign person to something actually.
FOR EACH person IN family
Forms!CheckIn![PASSHOLDER] = Me![BARCODE]
Forms!CheckIn![CHECKINTIME] = Now()
DoCmd.GoToRecord , , acNewRec
NEXT
DoCmd.Close acForm, "CheckIn", acSaveYes
This is how you iterate over a collection:
https://msdn.microsoft.com/en-us/library/y0e76axa(v=vs.100).aspx

Passing a query a parameter [Access 2016]

To make a longer story shorter:
I'm an Access noob, doing a quick-and-dirty conversion of a massive Excel spreadsheet into an Access database. Part of the requirements are to mimic some of the functionality of Excel, specifically, pulling data from a certain table and doing some basic calculations on it (sums, averages, etc.).
I've written a chain of queries to pull the data, count/sum it, etc., and have been testing them by using a manually-entered Parameter (i.e., the kind where the input box pops up and asks you to type a response). Now that I'm ready to drop these queries into a (sub)form, though, I have no idea how to automatically pass that parameter from a box in the form into the subform into the query.
Every query I've written uses a manually-entered Parameter named "MATCHNAME," which holds the name of an individual. In manual testing, if I enter this parameter on one query, all the queries it calls also get that value. So, I think I just need to figure out how to tell the top query what MATCHNAME actually is, and that'll take care of it.
Problem is, I don't know how to do that in Access. If it was any other programming language, I'd do something like "queryXYZ(MATCHNAME);", but I don't think I can do that in Access. Plus, since the values queryXYZ returns are all calculated, I'm not sure how to add an extra MATCHNAME field, nor how to actually make sure that gets read by the queries, nor how to make sure it gets passed down the chain. I've even tried creating a Parameter in design view, then trying to set up Link Master Fields, but the Parameter doesn't appear in that window.
I'd also like to re-run these queries whenever a new record is pulled up, but I'm not sure how to do that either--i.e., the numbers should be current for whatever record I'm looking at.
And, before we go there--I feel like a Relationship is out of the question, as the data itself is auto-generated, and is in rough enough shape to where I can't guarantee that any given key is wholly unique, and large enough (20k+) that, outside of writing a magical script, I can't assign a numerical key. However, I don't know much about Relationships in Access, so please prove me wrong.
(Is this all making sense?)
Do you have any suggestions for me--for how to make a subform read a field on the main form to run its queries on? Alternately, is there an easier way to do this, i.e., to bed SQL calls inside a form?
Thanks very much for your help...
You can use SQL as the recordsource of the subform in the property tab and use the afterupdate event of your matchname field to change yourform.recordsource = "Select * from table where filteredfieldname = & me.matchname & ";" . You can also use sql as the control source of form fields. To pass criteria to filter the subform using the whole table as the recordsource, add an event procedure to your field's after update event like this
`In the declarataions at the top
Global mtchnmfltr as string
Private Sub MATCHNAME_AfterUpdate()
'use the same procedure for Private Sub yourmainform_Current()
mtchnmfltr = "[yourfilterfield] = " & Chr(34) & me.matchname & Chr(34)
'if matchname is not text then just = "[yourfilterfield] = " & me.matchname
with me.subformname.form
.filter = mtchnmfltr
.filteron = true
end with
'Build your sql as a string for your sum avg fields etc. using mtchnmfltr in the where clause
me.yoursumfield.controlsource = "Select...where " & mtchnmfltr & ";"
'etc.
end sub
Or you could throw Matchname into a sql recordsource of the subform and add the function fields to the subform on the same on current and after update events
if me.newrecord = true then
me.dirty = false
end if
me.subform.form.recordsource = "Select Table.Matchname, sum(yourfield) as sumalias, _
(etc.) from yourtable where table.matchname = " & chr(34) & me.matchname & _
chr(34) & Group By table.matchname"
If you are storing your sums etc in a table you need to do it a bit different, since your controls controlsource are bound to fields.
dim strsqlsumfld as string
dim rs as dao.recordset
strsqlsumfld= "Select SUM.....AS sumfldalias where " & mtchnmfltr & ";"
set rs = currentdb.openrecordset(strsqlsumfld)
me.yoursumfield = rs("sumfldalias")
rs.close

Use toggle buttons to update a record in Access

Hopefully not too difficult question but I cannot figure this out and have been unable to find anything searching the forums.
I want to convert the output of my toggle boxes from 1,2,3,4,5 to the text each button displays.
Couldn't find any setting on the toggle boxes properties themselves so decided I would have to write a macro/vba to do it from the table but as it's quite new to me I am struggling on syntax.
I have tried doing this on the inbuilt data macro mainly, but also tried it via a query and vba and still couldn't figure it out.
[don't have any reputation yet so have not been allowed to post pics of my macro attmept]
please help! Any solution using vba, data macro or a query would be great
EDIT
To be specific rather than a message box I want to update field1 in my table "Major Equipment" based off the option group selection this is my latest attempt but still not sure how to reference the option group. Do I need to set grp equal to the option group and if so how? Is it something like forms!myform!optiongroup ?
Option Compare Database
Function MyFunc(grp As OptionGroup)
Dim rcrdset As Recordset
set grp =
Set rcrdset = Application.CurrentDb.OpenRecordset("Major Equipment", dbOpenDynaset)
Select Case grp.Value
Case 1
With rcrdset
.AddNew
![Field1] = "Not Reviewed"
Case 2
.AddNew
![Field1] = "Reviewed"
Case Else
MsgBox "Error"
End Select
End Function
Also just realised since these toggle buttons will be updated by the user and so I probably need an update rather than addnew?
http://i59.tinypic.com/2ym8wet.jpg
Your buttons are part of an Option Group. That is what you must reference. Below is a snippet from my net search.
From the AfterUpdate() event of your Option Group:
Call MyFunc(Me.MyGroup)
... which will use Select Case to evaluate:
Function MyFunc(grp As OptionGroup)
Select case grp.Value
Case 1
MsgBox "Option value is 1."
Case 2
MsgBox "Option value is 2."
Case Else
MsgBox "Huh?"
End Select
End Function
If you are entirely new to VBA, there are a half-dozen things to learn here, but they will serve you well. VBA provides a bit less-friendly-looking start than a macro, but I can tell you have more adventures ahead and I suggest you skip macros. For most needs, VBA will serve you better; and it's much easier to trouble shoot or provide details when you need advice.
To convert this to a useful function, you will fill a string variable rather than raising a message box. Then you can use the string variable to do something like run an update query. Your latest edit suggests you will go for something like:
strSQL = "UPDATE [Major Equipment] " _
& "SET Field1='" & strUserSelection & "' " _
& "WHERE MyID=" & lngThisRecord
DoCmd.RunSQL strSQL
Your last edit proposes using a DAO recordset. I think you might be fine with the humble DoCmd. Less to learn. You can hammer out a prototype of the query in good ol' Access; then switch to SQL View and paste the query into your VBA module. Insert variables as seen above, taking care with the quote marks. If it doesn't work, use Debug.Print to grab the value for strSQL and take that back to good ol' Access where you can poke at it into shape; use your findings to improve the VBA.