My project is a small Access database with two tables. The first table is "EMPLOYEES" and the second table is "TOOLS". The database keeps track of what specific tools each employee has in his/her possession. My Visual Basic form adds and deletes employees from the database via the dataset.
What I would like to do is set a variable to hold the Employees full name taken from two different fields. (The "FirstName" field and the "LastName" field)
How do I reference (or get the value of) the "FIRST_NAME" and "LAST_NAME" data stored in the "EMPLOYEE" table so that I can set my Visual Basic string Variable with those two values.
Is this a job for SQL?
Something like this:
For i As int32 = 0 To myDATABASE.EMPLOYEES.column.Length
Dim fullName As String = _
myDATABASE.EMPLOYEES.row(i).FIRST_NAME _
& " " & _
MyDATABASE.EMPLOYEES.row(i).LAST_NAME
mylistBox.add("The full name is: " & fullName)
Next
If you are trying to populate a listbox with names, then you can set the recordsource to something like:
Select [First_Name] & " " & [Last_Name] as FullName from Employees
You can concatenate the names in sql as Wayne G. Dunn suggests or you can also do it in vb like this:
For i As Int32 = 0 To MyDATABASE.Tables("EMPLOYEES").Rows.Count - 1
Dim fullName As String = _
MyDATABASE.Tables("EMPLOYEES").Rows(i).Item("FIRST_NAME").ToString() _
& " " & _
MyDATABASE.Tables("EMPLOYEES").Rows(i).Item("LAST_NAME").ToString()
mylistBox.add("The full name is: " & fullName)
Next
For something as simple as putting two strings together it's really a matter of personal preference and it's good to know both ways.
Related
I have a form built off of an Outer-Joined Query. I cannot enter data in one field, Syteline_Customer_ID, since after opening the form some calculations are done and that calculated data is then entered into the fields on the other side of this join. I think I have a work around - I was trying to run an Update Query on Form load that would update Tbl_JobTicketHeader with the required data so it will populate when the form is opened. Below is the structure of the related fields and tables:
Tbl_JobTicketHeader (This is the table I am trying to update)
Access_ID - Autonumber (PK)
Syteline_Customer_ID - Number [Integer] (FK) & (Field trying to update)
It is getting this ID based off of the Customer Name selected on the form. I have created a Query to search for the Customer_Name field in my form as Criteria so I only bring up the related Customer. The table structure looks like:
Tbl_MasterCustomerList (Table Query is built off of)
Customer_ID - Number [Integer] (PK) - (Value is assigned from a billing system, cannot autonumber)
Customer_Name - Short Text
Below is my code:
Dim ID As Integer
Dim frm As Access.Form
Set frm = Forms!Frm_JobTicket
Dim SQL As String
ID = DLookup("[Customer_ID]", "Qry_CustomerID", "[Customer_Name] = '" & frm("Customer_Name") & "'")
SQL = "UPDATE Tbl_JobTicketHeader " _
& "SET Syteline_Customer_ID = '" & ID & "'" _
& " WHERE Access_ID = '" & frm("Part_Number") & "'"
DoCmd.RunSQL (SQL)
End Sub
Any help on this would be very appreciated, I have debug.print'ed out ID and everytime it comes out to an Integer such as 2548, 9999, 5862, etc. I am not quite sure where to go from here or what could be wrong.
This almost works. I get an error at the last line that looks like it's complaining about the C1 reference. Is there a simple way around this? There is nothing wrong with the query or connection.
Dim CmdString As String
Dim con As New SqlConnection
Try
con.ConnectionString = PubConn
CmdString = "select * from " & PubDB & ".dbo.Suppliers as S " & _
" join " & PubDB & ".dbo.Address as A" & _
" on S.Supplier_Address_Code = A.Address_IDX" & _
" join " & PubDB & ".dbo.Contacts as C1" & _
" on S.Supplier_Contact1 = C1.Contact_IDX" &
" join " & PubDB & ".dbo.Contacts as C2" & _
" on S.Supplier_Contact2 = C2.Contact_IDX" &
" WHERE S.Supplier_IDX = " & LookupIDX
Dim cmd As New SqlCommand(CmdString)
cmd.Connection = con
con.Open()
Dim DAdapt As New SqlClient.SqlDataAdapter(cmd)
Dim Dset As New DataSet
DAdapt.Fill(Dset)
con.Close()
With Dset.Tables(0).Rows(0)
txtAddress1.Text = .Item("Address1").ToString
txtAddress2.Text = .Item("Address2").ToString
txtSupplierName.Text = .Item("Address_Title").ToString
txtAttn.Text = .Item("Attn").ToString
txtBusinessPhone1.Text = .Item("C1.Contact_Business_Phone").ToString
You would not include the "C1" table alias as part of your column name. It will be returned from your query as Contact_Business_Phone.
For accessing multiple rows you could use the indexer as you are in the example above "Rows(0)" by placing your With block into a For loop and accessing the "Rows(i)" with your loop variable. However, this would not help much as your are assigning this to individual text boxes, so you'd only see the last value on your page/screen.
The alias C1 is used by SQL Server and is not persisted to the result set. Have you taken this query into SQL Management Studio to see the results?
Since you requested all columns (*) and joined to the Contacts table twice, you'll end up with duplicate column names in the result. For example, if the Contacts table has a LastName field, you'll end up with TWO LastName columns in your result.
I haven't tried to duplicate this in my local environment, but I can't imagine the data adapter is going to like having duplicate column names.
I recommend specifically including the columns you want to return instead of using the *. That's where you'll use the alias of C1, then you can rename the duplicate columns using the AS keyword:
SELECT C1.LastName AS [Supplier1_LastName],
C2.LastName AS [Supplier2_LastName],
...
This should solve your problem.
Good Luck!
You should only be pulling back the columns that you're in fact interested in, as opposed to *. It's sort of hard to tell exactly what data exists in which tables since you're pulling the full set, but at a quick guess, you'll want in your select statement to pull back A.Address1, A.Address2, A.AddressTitle, ?.Attn (not sure which table this actually derives from) and C1.Contact_Business_Phone. Unless you actually NEED the other fields, you're much better off specifying the individual fields in your query, besides having the possible duplicate field issue that you're running into here, it can also be a significant performance hit pulling everything in. After you clean up the query and only pull in the results you want, you can safely just reference them the way you are for the other fields, without needing a table alias (which as others have pointed out, isn't persisted to the result set anyways).
Is it possible to write a query to loop through the rows of a two column table, checking the first column for a certain identifier and copy the data in the second column into a new table?
Example:
tblSurveyData
FirstColumn Second Column
A0 John
A2 Smith
A3 05-01-1973
tblSurveyReport
FirstName MiddleName LastName DateOfBirth
John Smith 05-01-1973
Where A0 data would go to FirstName, A1 MiddleName, A2 LastName and A3 DateOfBirth. There are many more identifiers and fields but just as an example how would you do this with a query in Access or is VBA a better solution?
The only solution I came up with is the following VBA but this bypasses the two column table and tries to insert into the tblSurveyReport table. Unfortunately, it puts each piece of data into its own row which doesn't help.
If Identifier = "A0" Then
DoCmd.RunSQL "INSERT INTO tblSurveyReport " _
& "(FirstName) " _
& "VALUES ('" & Info & "')"
ElseIf Identifier = "A1" Then
DoCmd.RunSQL "INSERT INTO tblSurveyReport " _
& "(MiddleName) " _
& "VALUES ('" & Info & "')"
ElseIf Identifier = "A2" Then
DoCmd.RunSQL "INSERT INTO tblSurveyReport " _
& "(LastName) " _
& "VALUES ('" & Info & "')"
ElseIf Identifier = "A3" Then
DoCmd.RunSQL "INSERT INTO tblSurveyReport " _
& "(DateOfBirth) " _
& "VALUES ('" & Info & "')"
End If
However each piece of data is going into its own row and I need it all in the same row.
Any help is greatly appreciated.
Thanks in advance.
TC
Use INSERT INTO with a SELECT statement
INSERT INTO tblSurveyReport(FirstName) SELECT FirstName FROM tblSurveyData where FirstColumn = 'A0'
INSERT INTO tblSurveyReport(MiddleName) SELECT MiddleName FROM tblSurveyData where FirstColumn = 'A1'
You could run this using a DoCmd, as a query in Access, etc.
You will need something that would link your records together. What happens if the data gets re-sorted? How would you know that all the info in your example should be in the same record? I believe the only way to do something like this would be to create a 3rd field in your first table that determines which data belongs with which, something like a UserID. Then the table would look like this:
tblSurveyData
FirstColumn Second Column UserID
A0 John XX001
A2 Smith XX001
A3 05-01-1973 XX001
Then you could create a preliminary query like:
Select DISTINCT UserID from tblSurveyData
Use that as your "pointer" query and loop through the results, and then you can pull all the records with each UserID out and copy them into the new table. Or, you can inner join the "pointer" query and tblSurveyData, and then use a Crosstab query. The easiest way to do that would be to use the wizard to create it, and then just copy the code into your VBA.
EDIT: For easier readability for future readers, the SQL for the query you're asking for in your comment is:
SELECT Max(IIf([Identifier]="A0",[IValue],"")) AS FName, Max(IIf([Identifier]="A1",[IValue],"")) AS MName, Max(IIf([Identifier]="A2",[IValue],"")) AS LName, Max(IIf([Identifier]="A3",[IValue],"")) AS BDate FROM tblSurveyData;
You will need to change "First Column" in your sample data to "Identifier", and "Second Column" to "IValue" (or make the corresponding field name changes to the above SQL). I have tested this and it worked perfectly, giving you one record with all 4 values in corresponding fields. Once you've pasted that into a blank query, you can switch to Design View and change the query to an Append or MakeTable query and that will let you export the results to tblSurveyReport.
You could have simply two recordsets, like this:
1st recordset: rsSurveyData
2nd recordset: rsSurveyReport
The idea is to browse the first recordset along its records, and the second along its fields.
depending on the recordset object you are using (DAO or ADODB), the opening syntax will be slightly different, but you'll find all details in the help. I am using here the ADODB syntax for the 'Find', 'Move', and 'Update' methods. DAO recordsets need an extra 'Edit' method before changing the data. And I do not remember if the fields collection of the recordset object is indexed from 0 or 1 ...
Then:
rsSurveyData.moveFirst
rsSurveyReport.Find "FirstName ='" & rsSurveyData.fields("A0") & "'"
if rsSurveyReport.EOF then
'do what you have to do when the record does not exist, for example:'
rsSurveyReport.addNew
rsSurveyreport.fields(1) = rsData.fields(1)
Else
'you''ve just found the record that needs to be updated'
for i = 2 to rsSurveyData.recordcount
rsSurveyData.move i, 1
rsSurveyReport.fields(i)=rsSurveyData.fields(1)
next i
rsSurveyReport.update
Endif
Making GUI in Access with VBA (first time i saw it in this semester and it looks unusual for me). I got table Authors where i got columns author_id, last_name, first_name and table Books with columns author_id, book_name.
I got button on Form which on click should ask user to input author last name and then search and show all books of this author.
So I trying to find author id from Authors table and then from Books table show all books where author.author_id is equal to books.author_id.
I was thinking that i need to create temp query which contained author_id value and after that create record set with this query using SQLquery like "SELECT [Books].[book_name] AS [Bookname] FROM [Books] WHERE [Books].[author_id] = [test].[ID]" But i stucked here - I trying to just check if this thing working but it says there is an error 3061
Private Sub authorlist_Click()
Dim dbs As Database, authorsRS, booksRS As Recordset, queryStr, idbynameQuery, srchASurStr, strOutput, srId As String, qdf As QueryDef
Set dbs = CurrentDb()
srchASurStr = InputBox("Input author surname, please", , , 100, 100)
strQuery = "SELECT [Authors].[author_id] AS [ID] FROM [Authors] WHERE [Authors].[last_name] = " & srchASurStr & ""
Set authorsRS = dbs.OpenRecordset(strQuery, dbOpenSnapshot)
With dbs
Set qdf = .CreateQueryDef("test", strQuery)
DoCmd.OpenQuery "test"
.QueryDefs.Delete "test"
End With
End Sub
So could you help me please to understan what's wrong? And is there maybe more simple way to show all books of some author (maybe without using SQL querys)?
String values in an SQL statement need to be surrounded with single-quotes (') or double-quotes ("):
SELECT author_id FROM authors WHERE last_name = "Smith"
If written without the quotes:
SELECT author_id FROM authors WHERE last_name = Smith
Smith will be understood to be a field name and not a string value. So your code should look something like this:
'Chr returns a string from a character code. 34 is the code for "
strQuery = "SELECT author_id FROM authors WHERE last_name = " & Chr(34) & srchASurStr & Chr(34)
In VBA, you can escape double-quotes with a string by doubling them:
strQuery = "SELECT author_id FROM authors WHERE last_name = """ & srchASurStr & """"
SQL injection: Keep in mind that if the user inputs a string with " in it, there will probably be an error, as the resulting SQL statement has invalid syntax:
SELECT author_id FROM authors WHERE last_name = "Smi"th"
The right way to avoid this problem is to use parameters.
Some notes:
You can reference a form control within a query: [Forms]![FormName]![ControlName]. Thus, you can create a saved query that filters based on a form textbox, instead of using an inputbox.
Consider using a combobox to have the user select from a list, instead of having the user type free text. The combobox can have multiple columns, with the value of the combobox being the first column (author_id) and the displayed value being another expression (last_name or last_name & " " & first_name). If you set the ColumnWidths property to 0 (for the first column), the next column will be displayed
If you prefer to use a textbox, consider using the LIKE operator in your query, to display all authors whose last_name contains the user string:
SELECT author_id FROM authors WHERE last_name LIKE "%sm%"
will return Smith, Smythe, and Asmodeus.
I suggest you set up a form and subform. The form can contain author details and the subform can contain books by that author, you can further add a textbox that the user can fill in with part of the author name. You can then apply a filter to the main form to show all the authors with that name.
Me.Filter = "Author Like '*" & Me.Search & "*'"
Me.FilterOn = True
There are a number of variations on this, the user could select names from a combo or listbox. The form could be a continuous form with a filter / search in the header and so on.
I'm having some issues with some functionality of my application. There is a particular instance where I have an instance of a 'pending class' on a form for an administrator to review. The form is populated with students associated with this pending class. After their grades are finished, I have a button at the footer that will delete this class from my 'pending' table and add the grades to all of the students. This works.
However, I want to essentially copy this pending class, which just has the class name, date, and teacher to a completed class table before it's deleted from pending. Since no data about this class other than the primary key(class number) persists throughout this form, I can't populate the other fields(class name, date) of the row into my completed class table.
I am trying a "SELECT INTO" operation in VBA to get these values. It's going like this:
dim cname as String
dim classdate as Date
dim pid as integer
dim teacher as String
dim qry as String
pid = [Forms]![frmClasses]![txtID]
qry = "Select className INTO cname FROM tblPending WHERE tblPending.id = " & " ' " & pid & " ' " & ";"
db.execute qry
debug.print qry
debug.print cname
From here, I do the same operations for each other variable, build my INSERT query, and execute it. The problem is-- my select into's are not working. Debug.print shows that the local variables were never initialized from the SELECT INTO statement. Any thoughts?
First, having all classes in one table and just setting a "NotPending" or "Completed" column would be better.
Having two identical tables for classes and moving values from one into the other to indicate status changes is bad database design.
If you really need to do this by using two tables and copying rows, then you need an INSERT INTO query (and not SELECT INTO), as already mentioned by Remou in the comments, because SELECT INTO creates a new table (or overwrites an existing one with the same name, if already there).
The syntax for INSERT INTO looks like this:
INSERT INTO CompletedClassTable (ClassName, Teacher)
SELECT ClassName, Teacher FROM tblPending WHERE id = 123
And finally, you asked this in a comment:
So SELECT INTO is completely different in Access than Oracle? In Oracle and PL/SQL, you can select a row into a variable OR a table. In Access can you not select into a variable?
To load a row into a variable, you need to use a Recordset.
Example code to load your query into a Recordset and output the ClassName field:
Dim RS As DAO.Recordset
Set RS = CurrentDb.OpenRecordset("SELECT * FROM tblPending WHERE id = 123")
If Not RS.EOF Then
Debug.Print RS("classname")
End If
RS.Close
Set RS = Nothing
Seems you want to retrieve a text value, className, from tblPending where tblPending.id matches the value found in your text box, txtID, and store that text value in a string variable named cname.
If that interpretation is correct, you needn't bother with a query and recordset. Just use the DLookup Function to retrieve the value, similar to this untested code sample.
Dim cname As String
Dim pid As Integer
Dim strCriteria As String
pid = [Forms]![frmClasses]![txtID]
strCriteria = "id = " & pid
cname = Nz(DLookup("className", "tblPending", strCriteria), vbNullString)
Debug.Print "cname: '" & cname & "'"
Notes:
I assumed the data type of the id field in tblPending is numeric. If it is actually text data type, change strCriteria like this:
strCriteria = "id = '" & pid & "'"
DLookup() returns Null if no match found. Since we are assigning the function's return value to a string variable, I used Nz() to convert Null to an empty string. Alternatively, you could declare cname As Variant (so that it can accept a Null value) and get rid of Nz().