Querying multiple tables and displaying the outcome wtih SQL and Access - sql

I need to display the customers that have purchased a product (based on a user search) in a list box. I have five different tables in Access which store different information and that relate to each other with IDs (using combox boxes in vb). I need to be able to search for a product, for example "White Bread", the program then should display the customer's full name and address as stored in the database.
Table: TransactionDetails
Fields: ID, stockID, custTransID
Table: CustomerTransaction
Fields: ID, custID, dateOfTransaction
Table: CustomerAccountDetails
Fields: ID, custFullName, custAddress, custLandline,
custMobile, custDOB, custCreditDetails
Table: StockDescription
Fields: ID, stockName, stockDesc, stockPrice
Table: SupplierDetails
Fields: ID, supplierName, supplier Address
I think I need to use INNER JOIN to query multiple tables at once but I am unsure of the syntax (I'm new to SQL). So far I have this:
Dim productSearch As String
productSearch = productSrchInput.Text
Dim databaseConnection As New OleDb.OleDbConnection
Dim counter As Integer
Dim array(10) As String
databaseConnection.ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=assignment5database.accdb"
databaseConnection.Open()
Dim searchDatabase As OleDbCommand = New OleDbCommand("SELECT CustomerAccountDetails.custFullName, CustomerAccountDetails.custAddress " & _
"FROM CustomerAccountDetails " & _
"INNER JOIN StockDescription ON TransactionDetails.stockID = TransactionDetails.custTransID " & _
"WHERE StockDescription.stockName = '" & productSearch & "'", databaseConnection)
Dim searchResults As OleDbDataReader = searchDatabase.ExecuteReader
counter = 1
Do While searchResults.Read
srchResultsList.Items.Add(searchResults.Item(0))
counter += 1
Loop
databaseConnection.Close()

You are missing some of the joins that connect the customer to the stock details. Here is the SQL that Access will expect in order to pull the data based on your description. The parentheses may seem extraneous, if you are used to SQL server or MySQL, but Access will throw a fit if you leave them out.
SELECT CustomerAccountDetails.custFullName, CustomerAccountDetails.custAddress, StockDescription.stockName
FROM StockDescription
INNER JOIN ((CustomerAccountDetails
INNER JOIN CustomerTransaction ON CustomerAccountDetails.ID = CustomerTransaction.custID)
INNER JOIN TransactionDetails ON CustomerTransaction.ID = TransactionDetails.custTransID) ON StockDescription.ID = TransactionDetails.StockID
WHERE StockDescription.stockName="something"
As noted by Fionnuala, I will almost always build a query that has multiple joins in it using the Access query designer before putting it in code. I almost always leave out a set of parentheses or try to write the query in a structure that SQL Server would expect and get rejected by Access.

I think you could be able to use an inner join but perhaps a "union" may be more efficient.
http://www.w3schools.com/sql/sql_union.asp is good for improving sql knowledge, it helped me a lot.

Related

Dynamic Selection Query with Criteria in Ms Access | SQL

I have a query in which I select multiple values. To create my form.
The results look like this :
the last Column [Candidat Nom] is a drop down list with an other query (lets call it Query 1) that select my drop down list values.
The selection is good and is what I'm looking for. Except I get the same value for all lines when they need to be different.
To simplify
let's take the following exemple
I have the following candidate that wants to join for the following job (Represented with their ID).
As we can see 2 candidates wants job N° 12. and 1 candidate for each other job.
What I get
All candidates are listed for every job.
What I want
What I actually did
is I put the following query (Query 1) on my column.
SELECT T_SALARIE.SALARIE_nom & " " & T_SALARIE.SALARIE_prenom AS Candidat Nom
FROM T_EMPLOI INNER JOIN (T_SALARIE INNER JOIN (T_SALARIE_EMPLOI LEFT JOIN T_STATUT_EMPLOI ON T_SALARIE_EMPLOI.SALARIE_EMPLOI_statut_id = T_STATUT_EMPLOI.STATUT_EMPLOI_id) ON T_SALARIE.SALARIE_NNI = T_SALARIE_EMPLOI.SALARIE_EMPLOI_salarie_nni) ON T_EMPLOI.EMPLOI_identifiant = T_SALARIE_EMPLOI.SALARIE_EMPLOI_emploi_identifiant
WHERE (((T_STATUT_EMPLOI.STATUT_EMPLOI_statut) Like "*valid*" Or (T_STATUT_EMPLOI.STATUT_EMPLOI_statut) Like "*décidé*") AND ((T_EMPLOI.EMPLOI_entreprise_id)=1));
This gave me the result I want but with the issue I mentioned previously (Same result for each line)
So
I thought I needed a new Criteria.
I added one, where It's going to select the candidate when the two "emploi ID" of my actual table (Shown before) and the one helping me select the candidates are equal.
With the following query:
SELECT T_SALARIE.SALARIE_nom & " " & T_SALARIE.SALARIE_prenom AS Candidat, T_SALARIE_EMPLOI.SALARIE_EMPLOI_emploi_identifiant
FROM T_EMPLOI INNER JOIN (T_SALARIE INNER JOIN (T_SALARIE_EMPLOI LEFT JOIN T_STATUT_EMPLOI ON T_SALARIE_EMPLOI.SALARIE_EMPLOI_statut_id = T_STATUT_EMPLOI.STATUT_EMPLOI_id) ON T_SALARIE.SALARIE_NNI = T_SALARIE_EMPLOI.SALARIE_EMPLOI_salarie_nni) ON T_EMPLOI.EMPLOI_identifiant = T_SALARIE_EMPLOI.SALARIE_EMPLOI_emploi_identifiant
WHERE (((T_STATUT_EMPLOI.STATUT_EMPLOI_statut) Like "*valid*" Or (T_STATUT_EMPLOI.STATUT_EMPLOI_statut) Like "*décidé*") AND ((T_SALARIE_EMPLOI.SALARIE_EMPLOI_emploi_identifiant)=[R_Select_COMOB]![ACTION_identifiant_emploi]));
But I keep on getting the following pop up that asks me to enter a Job ID
So how can I make the query for each line compare and select the right values?
I hope I was clear in explaining. If not please let me know so that I can add more details.
Thank you !
Thanks to your help and specially #June7 for his propositions, I found a solution regarding my problem :
I added a criteria to select values based on JobID that I wasn't selecting in the first hand.
And then based on the column (jobID) select the values needed
here is my final query :
SELECT
[SALARIE_nom] & " " & [SALARIE_prenom] & " (" & [SALARIE_NNI] & ")" AS Salarié, T_SALARIE_EMPLOI.SALARIE_EMPLOI_salarie_nni, T_SALARIE_EMPLOI.SALARIE_EMPLOI_id, T_SALARIE_EMPLOI.SALARIE_EMPLOI_emploi_identifiant
FROM
(T_STATUT_EMPLOI INNER JOIN T_SALARIE_EMPLOI ON T_STATUT_EMPLOI.STATUT_EMPLOI_id = T_SALARIE_EMPLOI.SALARIE_EMPLOI_statut_id) LEFT JOIN R_Select_Salarie ON T_SALARIE_EMPLOI.SALARIE_EMPLOI_salarie_nni = R_Select_Salarie.SALARIE_NNI
WHERE
(((T_SALARIE_EMPLOI.SALARIE_EMPLOI_emploi_identifiant)=[Formulaires]![F_COMOB]![ACTION_identifiant_emploi]) AND ((T_STATUT_EMPLOI.STATUT_EMPLOI_statut) Like "*validé*") AND ((T_SALARIE_EMPLOI.SALARIE_EMPLOI_Entreprise) Like "*RTE*"));
And Then to update my values for each line. I added a VBA code that requery on input.
Private Sub ACTION_Candidats_P_Enter()
ACTION_Candidats_P.Requery
End Sub
With that my problem is solved.

SQL query to display rows of 2 different tables

I'm trying to figure out how to pull all rows from two different tables with the OutageID = X. The purpose of this is to view the current outage and all the revisions in one statement to see all of the changes. Below is an example I was trying. However, it puts it all in one row. I want it to display all rows separately like you would if you were to query SELECT * From Table WHERE X = Y.
The Current Outages are in one table and the history is in another so they are not written over and not to change the design of the current DB.
Outages Table
`strSQL = "SELECT Outages.OutageID, Outages.Outage, Outages.Building,
Outages.OutageType, Outages.OutageStart, Outages.OutageStartTime,
Outages.OutageEnd, Outages.OutageEndTime, Outages.Duration,
Outages.Reason, Outages.Areas, Outages.Comment, Outages.ORN,
Outages.Contact, Outages.Phone, Outages.Job, Outages.Timestamp
FROM Outages
WHERE (((Outages.OutageID)=3305));"`
Outage History Table
`strSQL = "SELECT OutageHistory.RevisonID, OutageHistory.OutageID,
OutageHistory.Outage, OutageHistory.Building,
OutageHistory.OutageType,
OutageHistory.OutageStart, OutageHistory.OutageStartTime,
OutageHistory.OutageEnd, OutageHistory.OutageEndTime,
OutageHistory.Duration, OutageHistory.Reason, OutageHistory.Areas,
OutageHistory.Comment, OutageHistory.ORN, OutageHistory.Contact,
OutageHistory.Phone, OutageHistory.Job, OutageHistory.Timestamp
FROM OutageHistory
WHERE (((OutageHistory.OutageID)=3305));"`
`Private Sub All_Revision_Histoy_Click()
Dim strSQL As String
strSQL = "SELECT * From OutageHistory WHERE OutageHistory.OutageID = " &
Me.OutageID & ";"
Debug.Print strSQL
ShowDataSheet strSQL`
I think that I might need to create a temp table and insert both rows for the results and then Delete the table when its closed. However, I am not sure how to do that. I already feel I may of bitten off more than I can chew with this one. Thank you in advance.
select * from (
select 1 as revisionID, Outages.* FROM Outages
WHERE (((Outages.OutageID)=3305))
union
select OutageHistory.* FROM OutageHistory
WHERE (((OutageHistory.OutageID)=3305))
) order by revisionID desc

How to query twice at once from one table with inner join SQL ASP.NET

I have two tables first table called TFile contains two columns: FromCity and ToCity.
They will have different values but from one column of the second table (TCity) and specifically from the column called CityName.Second table name TCity they have two column : IdCity AND CityName.
My problem I need to display data for two columns they got from second table FromCity and ToCity with inner join for two times.
I use this code to do that:
SqlCommand comm = new SqlCommand("select * from TFile " +
"inner join TCity AS A ON TFile.FromCity = A.IdCity " +
"inner join TCity AS B ON TFile.ToCity = B.IdCity " + " WHERE " + "TFile.Name", con);
Then display data to users as:
SqlDataReader srd = comm.ExecuteReader();
if (srd.HasRows)
{
while (srd.Read())
{
//FromCity
TextFrom.Text = srd["CityName"].ToString();
//ToCity
TextTo.Text = srd["CityName"].ToString();//=======================here problem
}
}
In the first line of the data display I can get the name of the city but if I repeat that in the second line it will just repeat the data. Here problem.I can't use a different name to access the second query instead of the field name CityName.This is the name of the field in the second table for which I display the names of the cities.
How can I access to data in this query:
"inner join TCity AS B ON TFile.ToCity = B.IdCity
So if I access to it then can display second data in this line:
TextTo.Text = srd["CityName"].ToString();
How can solve this problem ?
I bet you need to create field aliases to differentiate between values from the multiple joins on the same table. By the way, please make sure you protect yourself from "SQL injection" when using these types of queries. If you don't know what "SQL Injection" is then please take some time to look it up.
"select FromCityName=A.CityName, ToCityName=B.CityName, * from TFile "
And
//FromCity
TextFrom.Text = srd["FromCityName"].ToString();
//ToCity
TextTo.Text = srd["ToCityName"].ToString();//=======================here problem

VB.NET LINQ to XML Left Join with ambiguous column name

I need to write a LINQ TO XML query, which queries two XML files exported from Access database tables. The original Access DB query looks like this:
SELECT
(
[TableB].[Code] Is Null,[TableA].[Code],
LCase(Left([TableA].[Code],1)) & ":" & [TableB].[code]
) AS Code,
Trim
(
[TableB].[Description] & " " & [TableA].[Description]
) AS Description
FROM TableA LEFT JOIN TableB
ON TableA.Code = TableB.SubProduct;
When I convert it to LINQ to XML, I have the problem of the right part of the left join is not available. My LINQ look like this:
Dim results = _
From a In TableA.Descendants("Product")
Group Join b In TableB.Descendants("Product")
On a.Element("Code").Value Equals b.Element("SubProduct").Value Into leftJoinGroup Group
From p In leftJoinGroup.DefaultIfEmpty
Select New With
{
I DON KNOW HOW TO WRITE IT
}
Both tables have the column named "Code". However, the variable TableB seems to be unavailable inside my Selectclause. I only have a and p available so I can't get the Code from TableB (b). How should I do that?
I just started using linq myself and ran into this issue last week. This was very helpful for me http://msdn.microsoft.com/en-us/vstudio/bb688088.aspx but here is an example of how to perform a left outer join in vb.net http://msdn.microsoft.com/en-us/vstudio/bb737909#lojoin.
In your example tableB is being stored into leftJoinGroup which your selecting from using p. To get values from tableB you will need to select from p and since your trying to concatenate the columns from both tableA and tableB, I would check if tableB record is null.
Dim results = From a In TableA.Descendants("Product") Group Join b In TableB.Descendants("Product") _
On a.Element("Code").Value Equals b.Element("SubProduct").Value Into leftJoinGroup = Group _
From p In leftJoinGroup.DefaultIfEmpty() _
Select New With { _
.Code = If(p Is Nothing, a.Element("Code").Value, String.Format("{0}:{1}", Left(a.Element("Code").Value.ToLower(), 1), p.Element("Code").Value)), _
.Description = If(p Is Nothing, a.Element("Description").Value, String.Format("{0} {1}", p.Element("Description").Value, a.Element("Description").Value))}
Here is an example of your code above, I didn't tested it. I've used this code when joining datatables not xdocuments. Sorry if this isn't clear this my first post here.

Excel VBA - INNER JOIN Issue

I've got an issue with an Inner Join statement I am using to access data from my Access Database. What I am expecting to happen is that I run through the record set for each product. When a product was ordered more then once, I dont add it to the excel sheet, instead I increment the number ordered and the total cost.
The problem I am having is that instead of it working the way I described above, it is adding a product to the excel sheet for every time it was ordered. I have discovered that it is printing the products in the order that they were ordered (By their OrderID) which is not included in my code.
Any help?
Here is the code :
Public Sub WorksheetLoop()
Dim cn As New ADODB.Connection
Dim rs As New ADODB.Recordset
Dim stDB As String, stSQL As String, stSQLTwo As String, stProvider As String
Dim sheetName As String, stProdName As String
Dim suppNum As Integer, prodNum As Integer
Dim WS_Count As Integer
Dim I As Integer
suppNum = 1
prodNum = 1
stDB = "Data Source= " & ThisWorkbook.Path & "\obsDatabase.accdb"
stProvider = "Microsoft.ACE.OLEDB.12.0"
'Opening connection to database
With cn
.ConnectionString = stDB
.Provider = stProvider
.Open
End With
' Set WS_Count equal to the number of worksheets in the active
' workbook.
WS_Count = ActiveWorkbook.Worksheets.Count
' Begin the loop.
For I = 2 To WS_Count
ActiveWorkbook.Worksheets(I).Range("A1") = "Company Name - " + ActiveWorkbook.Worksheets(I).Name + ""
ActiveWorkbook.Worksheets(I).Range("A2") = "Item Number"
ActiveWorkbook.Worksheets(I).Range("B2") = "Description"
ActiveWorkbook.Worksheets(I).Range("C2") = "Unit"
ActiveWorkbook.Worksheets(I).Range("D2") = "Cost Per Unit"
ActiveWorkbook.Worksheets(I).Range("E2") = "Quantity"
ActiveWorkbook.Worksheets(I).Range("F2") = "Total Cost"
ActiveWorkbook.Worksheets(I).Range("G2") = "Amount Remaining"
'Function to retrieve info!
stSQL = "SELECT Products.ProductID, Products.ProductName, Products.ProductDescription, Products.ProductUnit, LineItems.UnitPrice, LineItems.Quantity, LineItems.TotalPrice " & _
"FROM Products INNER JOIN LineItems ON LineItems.ProductID = Products.ProductID WHERE Products.SupplierID = " & suppNum & " "
rs.Open stSQL, cn
With rs
Do Until .EOF
If ActiveWorkbook.Worksheets(I).Range("A65536").End(xlUp) = rs.Fields("ProductName") Then
If ActiveWorkbook.Worksheets(I).Range("D65536").End(xlUp) = rs.Fields("UnitPrice") Then
ActiveWorkbook.Worksheets(I).Range("E65536").End(xlUp) = ActiveWorkbook.Worksheets(I).Range("E65536").End(xlUp) + rs.Fields("Quantity")
ActiveWorkbook.Worksheets(I).Range("F65536").End(xlUp) = ActiveWorkbook.Worksheets(I).Range("F65536").End(xlUp) + rs.Fields("TotalPrice")
End If
Else
ActiveWorkbook.Worksheets(I).Range("A65536").End(xlUp).Offset(1, 0) = rs.Fields("ProductName")
ActiveWorkbook.Worksheets(I).Range("B65536").End(xlUp).Offset(1, 0) = rs.Fields("ProductDescription")
ActiveWorkbook.Worksheets(I).Range("C65536").End(xlUp).Offset(1, 0) = rs.Fields("ProductUnit")
ActiveWorkbook.Worksheets(I).Range("D65536").End(xlUp).Offset(1, 0) = rs.Fields("UnitPrice")
ActiveWorkbook.Worksheets(I).Range("E65536").End(xlUp).Offset(1, 0) = rs.Fields("Quantity")
ActiveWorkbook.Worksheets(I).Range("F65536").End(xlUp).Offset(1, 0) = rs.Fields("TotalPrice")
End If
rs.MoveNext
Loop
End With
rs.Close
suppNum = suppNum + 1
ActiveWorkbook.Worksheets(I).Columns("A:A").EntireColumn.AutoFit
ActiveWorkbook.Worksheets(I).Columns("B:B").EntireColumn.AutoFit
ActiveWorkbook.Worksheets(I).Columns("C:C").EntireColumn.AutoFit
ActiveWorkbook.Worksheets(I).Columns("D:D").EntireColumn.AutoFit
ActiveWorkbook.Worksheets(I).Columns("E:E").EntireColumn.AutoFit
ActiveWorkbook.Worksheets(I).Columns("F:F").EntireColumn.AutoFit
ActiveWorkbook.Worksheets(I).Columns("G:G").EntireColumn.AutoFit
Next I
cn.Close
End Sub
I'm not sure if I am missing the point. But could you clarify your end goal? Does it have to be done via Excel VBA?
If what you are trying to achieve is a tab with each suppliers orders on, with one row per product and a total quatity for that product, then I would consider creating a query in the database itself and pass the supplier id parameter and any other parameters to the query. The query could then handle the grouping and counting of products and quatitys as this would be an aggregrate query.
This way you can have a refreshable query on each tab and wrtie VBA to refresh them individually or all together whichever suits your needs.
I would always try to avoid complex VBA coding as it's buggy at the best of times and becomes difficult to maintain once distributed.
Another option would be pull all the product data into another tab which you could Hide via VBA and use formula like SUMPRODUCT to display the information on the various tabs. Or use a combo style box to select your supplier and dynamically change the result set.
As I said in the beginning I may be missing the point, but if not and you would like help with my option(s) let me know, and if I am please clarify your question.
For your INNER JOIN issue, you would need to use an agregate query not a stright forward select. This is because your database (I assume) can have one supplier which can order the same product more than once (A one to Many relationship), from what you have supplied you have a quantity column in the LineItems table so I assume the duplicate product ID is from two or more seperate orders from the same supplier. Here is an example of the query, also consider aliasing your tables names, it makes following the code easier.
SELECT
p.ProductID
,p.ProductName
,p.ProductDescription
,p.ProductUnit
,SUM(l.UnitPrice)
,SUM(l.Quantity)
,SUM(l.TotalPrice)
FROM Products p
INNER JOIN LineItems l
ON l.ProductID = p.ProductID
WHERE p.SupplierID = 1
GROUP BY
p.ProductID
,p.ProductName
,p.ProductDescription
,p.ProductUnit
Regards
Kevin
You're only ever comparing the current product name to the last line added to the worksheet but nothing in your SQL query enforces any ordering to the results.
This would be OK if the data was returned like this:
ProductName, Quantity
foo, 7
foo, 4
bar, 3
but would not be OK if the data was returned like this:
ProductName, Quantity
foo, 7
bar, 3
foo, 4
You could use an ORDER BY clause and work with your current macro but, as other people have pointed out, you could use SQL to combine the data and this should be a simpler solution