How can I do a simple join on a subquery? - sql

I've been at this for 2 hours and have no idea what the issue is. Although I have worked a fair amount with SQL, I'm struggling with the idiosyncrasies in Access queries relative to SQL queries when using the ADO ACE connection to query Excel worksheets. My goal at it's most basic is to do a join with a subquery. I have simplified the query considerably to display the issue, so please excuse the fact that it wouldn't really make sense to run a query like this.
The error I keep getting is Syntax Error in From Clause. The worksheet only has 2 columns, Account Number and Cost
Sub stackoverflow()
Dim acctcon As New ADODB.Connection
Dim acctrec As New ADODB.Recordset
acctcon.ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Users\name\Documents\Book1.xlsx;" & _
"Extended Properties=" & Chr(34) & "Excel 12.0 Xml;HDR=YES" & Chr(34) & ";"
acctcon.Open
querystr = "Select [Accounts$].[Account Number], [mm].[Cost] " & _
"FROM [Accounts$] " & _
"JOIN (Select [Account Number], [Cost] " & _
"FROM [Accounts$]) As mm " & _
"ON [Accounts$].[Account Number] = [mm].[Account Number]"
acctrec.Open querystr, acctcon
'Syntax Error on From Clause
End Sub

Try to reset the Close bracket behind As mm
querystr = "Select [Accounts$].[Account Number], [mm].[Cost] " & _
"FROM [Accounts$] " & _
"INNER JOIN (Select [Account Number], [Cost] " & _
"FROM [Accounts$] As mm) " & _
"ON [Accounts$].[Account Number] = [mm].[Account Number]"
But you have to replace the inner join, because it makes no sense like #krish KM stated. I suppose you took it as a placeholder.

Related

MS Access SQL - Problem with subquery and function in ORDER BY clause

The senario is that some people apply for some positions.
So there are tApplicant, tPosition and a join table tPreferences for a many-to-many relationship between them.
I need to build a SQL expression where these should happen:
get some fields from a join of tApplicant and tPosition into a new table.
create a new field called AM which should be either 1 or 0.
1 = If tApplicant.applicationID is found in a third not relevant table called tInfo.
0 = If tApplicant.applicationID is not found there.
ORDER BY AM.
It is executed in VBA. This is what I 've got so far:
sSQL = "SELECT tApplicant.applicationID, tApplicant.name, tApplicant.ID, tPreferences.fld3, " & _
"NZ((SELECT 1 FROM tInfo WHERE tInfo.applicationID = tApplicant.applicationID), 0) AS AM " _
"INTO " & sTable & " FROM tPreferences INNER JOIN tApplicant " & _
"ON tPreferences.IDapplic = tApplicant.applicationID " & _
"WHERE tPreferences.IDposit = " & rsRos!ID & ";"
CurrentDB.Execute sSQL, dbFailOnError
It seems like step 3 cannot be done.
Adding ORDER BY AM, throws Run-time error '3061'. Too few parameters. Expected 1..
While adding ORDER BY NZ((SELECT 1 FROM tInfo WHERE tInfo.applicationID = tApplicant.applicationID), 0), throws Run-Time Error 3075: Syntax Error in Query Expression 'NZ((SELECT 1 FROM tAMEA WHERE tAMEA.aitisiID = tAiton.aitisiID), 0'..
If omitted, everything works fine but there's no ORDER BY.
How can I achieve this?
PS: If values 1 and 0 for AM make things complicated, and some other values instead could be easier to get with the query, it will be OK, I will deal with this in the rest of the code.
Nz is an application-level function (technically, a method of the Access.Application object exposed as a function. It is unavailable for use in DAO.
CurrentDb.Execute is using DAO.
There are two rewrite possibilities:
Rewrite to avoid Nz:
IIF((SELECT 1 FROM tInfo WHERE tInfo.applicationID = tApplicant.applicationID) IS NOT NULL, 1, 0)
Or my preferred rewrite:
ORDER BY EXISTS(SELECT 1 FROM tInfo WHERE tInfo.applicationID = tApplicant.applicationID) DESC
Rewrite to DoCmd.RunSQL which does allow these functions (and suppress warnings as desired:
DoCmd.RunSQL sSQL
First, Access might think that the subquery may return more than one record, thus Top 1 should be used.
Next, it makes no sense to order the records to be inserted, as records per definition carries no order unless you specify this in the target table or the query where that table is used as source.
Also, even if you insisted to sort the insert, Access can't do this, as it doesn't know the output for AM.
Thus, this will run:
sSQL = "SELECT tApplicant.applicationID, tApplicant.name, tApplicant.ID, tPreferences.fld3, " & _
"NZ((SELECT Top 1 1 FROM tInfo WHERE tInfo.applicationID = tApplicant.applicationID), 0) AS AM " _
"INTO " & sTable & " FROM tPreferences INNER JOIN tApplicant " & _
"ON tPreferences.IDapplic = tApplicant.applicationID " & _
"WHERE tPreferences.IDposit = " & rsRos!ID & ";"
CurrentDB.Execute sSQL, dbFailOnError
That said, Erik's suggestion to replace Nz with Exists is preferable as this will result in "clean SQL" which always will run faster:
sSQL = "SELECT tApplicant.applicationID, tApplicant.name, tApplicant.ID, tPreferences.fld3, " & _
"EXISTS (SELECT Top 1 1 FROM tInfo WHERE tInfo.applicationID = tApplicant.applicationID) AS AM " _
"INTO " & sTable & " FROM tPreferences INNER JOIN tApplicant " & _
"ON tPreferences.IDapplic = tApplicant.applicationID " & _
"WHERE tPreferences.IDposit = " & rsRos!ID & ";"
CurrentDB.Execute sSQL, dbFailOnError
Neither will this be sortable on AM. Also, it will in Access SQL return -1 and 0 for AM. To obtain 1, apply ABS:
sSQL = "SELECT tApplicant.applicationID, tApplicant.name, tApplicant.ID, tPreferences.fld3, " & _
"ABS(EXISTS (SELECT Top 1 1 FROM tInfo WHERE tInfo.applicationID = tApplicant.applicationID)) AS AM " _
"INTO " & sTable & " FROM tPreferences INNER JOIN tApplicant " & _
"ON tPreferences.IDapplic = tApplicant.applicationID " & _
"WHERE tPreferences.IDposit = " & rsRos!ID & ";"
CurrentDB.Execute sSQL, dbFailOnError
#ErikA and #Gustav 's answers pointed me to the right direction. Thank you both for your time.
The problem in this case was that I tried to use a subquery in the ORDER BY clause. Which I found out now that is not allowed. eg. see here
More over, I found this question, which makes mine a possible duplicate. Here it is suggested to wrap the query.
So I firstly SELECT the data in no order with the subquery and then, INSERT INTO the new table using ORDER BY with the new column of the subquery.
So I'm posting what finally worked for me.
sSQL = "SELECT * INTO " & sTable & " FROM (" & _
"SELECT tApplicant.applicationID, tApplicant.name, tApplicant.ID, tProtimisi.fld3, " & _
"NZ((SELECT 1 FROM tInfo WHERE tInfo.applicationID = tApplicant.applicationID), 0) AS AM " & _
"FROM tPreferences INNER JOIN tApplicant " & _
"ON tPreferences.IDapplic = tApplicant.applicationID " & _
"WHERE tPreferences.IDposit = " & rsRos!ID & ") " & _
"ORDER BY AM DESC;"
CurrentDb.Execute sSQL, dbFailOnError

Query holds on to old criteria & Subform won't refresh

I'm currently working on redesigning my company's access database from scratch, with no prior experience, because the last edits to its structure were made in 2006, and it's basically a list with 200k records that's slow ass hell. (current tests with working features are already showing significant improvements, yay!)
I tried to create a subform that displays the result of a query, but the query itself depends on a multiselect. The query acts on a customer Id and returns results based on the selected customers. (this works)
I've been looking and searching for a solution, but, i just can't figure out what to do.
The problem i'm dealing with is twofold, and i suspect related, but first a little bit of context.
First of all, the subform just refuses to update visually. While the query holds the desired results, the subform doesn't update at all, i've tried refresh, docmd.requery, you name it, the only thing that works is basically 'reattaching' the source. Any manner that managed to refresh the shown results are not accessible from the 'surface'.
Second, the query seems to 'hold on' to whatever clients were previously selected. Even though it can go from all clients (i have written this as a 'no selection means everything') to specific clients, in between selections, it keeps the criteria from the previous selection, and brings them forward.
'Dashboard' is the form in which the subform TabGeneralSubform is nested , qryTetraGeneral is the query used as a source for the subform. MultiCustomer is the multiselect form.
Sub ListAll_Click() [access vba]
Set MyDB = CurrentDb
flgSelectAll = 0
Set qdef = MyDB.QueryDefs("qryTetraGeneral")
'strSQL is based on the SQL conversion of the SelectClientAll query
'the final AND clause will be appended by strWhere which is based on the MultiList Selection
'If there is no selection the query will default to All active Subs
strSQL = "SELECT [EQ-TETRA_ISSI].ISSI,[EQ-TETRA_TEI].TEI, [EQ-TETRA_ISSI].Active," & _
" [EQ-TETRA_TEI].Activation, Client_Table.[Customer Name], Client_Table.[Customer ID], [EQ-TETRA_REQFUL].ReqType, SERVICEREQUEST_TABLE.REQNUM," & _
" SERVICEREQUEST_TABLE.REQDATE, SERVICEREQUEST_TABLE.RESPUSER" & _
" FROM ((Client_Table INNER JOIN [EQ-TETRA_ISSI] ON Client_Table.[External Customer Index] = [EQ-TETRA_ISSI].[Customer Index])" & _
" INNER JOIN SERVICEREQUEST_TABLE ON Client_Table.[External Customer Index] = SERVICEREQUEST_TABLE.Index) INNER JOIN ([EQ-TETRA_TEI]" & _
" INNER JOIN [EQ-TETRA_REQFUL] ON [EQ-TETRA_TEI].TEI = [EQ-TETRA_REQFUL].TEI) ON (SERVICEREQUEST_TABLE.REQNUM = [EQ-TETRA_REQFUL].Reqnum)" & _
" AND ([EQ-TETRA_ISSI].ISSI = [EQ-TETRA_REQFUL].ISSI)" & _
" WHERE ((([EQ-TETRA_TEI].TEI)=[EQ-TETRA_REQFUL].[TEI]) AND (([EQ-TETRA_ISSI].Active)=True) AND (([EQ-TETRA_TEI].Activation)=True) AND ("
'extracts selection from list and creates SQL line to be added to strSQL
For i = 0 To MultiCustomer.ListCount - 1
If MultiCustomer.Selected(i) Then
flgSelectAll = flgSelectAll + 1
strIN = strIN & "([EQ-TETRA_ISSI].[Customer Index]=" & MultiCustomer.Column(0, i) & ")" & " OR "
End If
Next
If flgSelectAll = 0 Then
strSQL = "SELECT [EQ-TETRA_ISSI].ISSI,[EQ-TETRA_TEI].TEI, [EQ-TETRA_ISSI].Active," & _
" [EQ-TETRA_TEI].Activation, Client_Table.[Customer Name], Client_Table.[Customer ID], [EQ-TETRA_REQFUL].ReqType, SERVICEREQUEST_TABLE.REQNUM," & _
" SERVICEREQUEST_TABLE.REQDATE, SERVICEREQUEST_TABLE.RESPUSER" & _
" FROM ((Client_Table INNER JOIN [EQ-TETRA_ISSI] ON Client_Table.[External Customer Index] = [EQ-TETRA_ISSI].[Customer Index])" & _
" INNER JOIN SERVICEREQUEST_TABLE ON Client_Table.[External Customer Index] = SERVICEREQUEST_TABLE.Index) INNER JOIN ([EQ-TETRA_TEI]" & _
" INNER JOIN [EQ-TETRA_REQFUL] ON [EQ-TETRA_TEI].TEI = [EQ-TETRA_REQFUL].TEI) ON (SERVICEREQUEST_TABLE.REQNUM = [EQ-TETRA_REQFUL].Reqnum)" & _
" AND ([EQ-TETRA_ISSI].ISSI = [EQ-TETRA_REQFUL].ISSI)" & _
" WHERE ((([EQ-TETRA_TEI].TEI)=[EQ-TETRA_REQFUL].[TEI]) AND (([EQ-TETRA_ISSI].Active)=True) AND (([EQ-TETRA_TEI].Activation)=True)) ORDER BY [EQ-TETRA_ISSI].ISSI;"
Else
strWhere = Left(strIN, Len(strIN) - 3) & ")) ORDER BY [EQ-TETRA_ISSI].ISSI;"
strSQL = strSQL + strWhere
End If
qdef.SQL = strSQL
Call CallRefreshForm([Forms]![Dashboard])
For Each ListObject In MultiCustomer.ItemsSelected
MultiCustomer.Selected(ListObject) = False
Next ListObject
Now, admittedly, the code is a bit cobbled together from what my predecessors left behind, and the SQL statement looks like a mess, however, it works and displays the desired results based on the criteria, which currently, is good enough for me. I'll probably try trimming it down later when i know all desired features are working.
There are several ways that this can be streamlined. Firstly when you build the SQL based on the list box, rather than use a lot of IN/OR, just use IN once (I'm air-coding a bit here, so forgive any slight errors)
For i = 0 To MultiCustomer.ListCount - 1
If MultiCustomer.Selected(i) Then
strSQL = strSQL & MultiCustomer.Column(0, i) & ","
End If
Next i
If Right(strSQL,1)="," Then strSQL=Left(strSQL,Len(strSQL)-1)
If Len(strSQL)>0 Then
strSQL="SELECT [EQ-TETRA_ISSI].ISSI,[EQ-TETRA_TEI].TEI, [EQ-TETRA_ISSI].Active," & _
" [EQ-TETRA_TEI].Activation, Client_Table.[Customer Name], Client_Table.[Customer ID], [EQ-TETRA_REQFUL].ReqType, SERVICEREQUEST_TABLE.REQNUM," & _
" SERVICEREQUEST_TABLE.REQDATE, SERVICEREQUEST_TABLE.RESPUSER" & _
" FROM ((Client_Table INNER JOIN [EQ-TETRA_ISSI] ON Client_Table.[External Customer Index] = [EQ-TETRA_ISSI].[Customer Index])" & _
" INNER JOIN SERVICEREQUEST_TABLE ON Client_Table.[External Customer Index] = SERVICEREQUEST_TABLE.Index) INNER JOIN ([EQ-TETRA_TEI]" & _
" INNER JOIN [EQ-TETRA_REQFUL] ON [EQ-TETRA_TEI].TEI = [EQ-TETRA_REQFUL].TEI) ON (SERVICEREQUEST_TABLE.REQNUM = [EQ-TETRA_REQFUL].Reqnum)" & _
" AND ([EQ-TETRA_ISSI].ISSI = [EQ-TETRA_REQFUL].ISSI)" & _
" WHERE [EQ-TETRA_TEI].TEI=[EQ-TETRA_REQFUL].[TEI] " _
& " AND [EQ-TETRA_ISSI].Active=True " _
& " AND [EQ-TETRA_TEI].Activation=True " _
& " AND [EQ-TETRA_ISSI].[Customer Index] IN (" & strSQL & ")
Else
strSQL="SELECT [EQ-TETRA_ISSI].ISSI,[EQ-TETRA_TEI].TEI, [EQ-TETRA_ISSI].Active," & _
" [EQ-TETRA_TEI].Activation, Client_Table.[Customer Name], Client_Table.[Customer ID], [EQ-TETRA_REQFUL].ReqType, SERVICEREQUEST_TABLE.REQNUM," & _
" SERVICEREQUEST_TABLE.REQDATE, SERVICEREQUEST_TABLE.RESPUSER" & _
" FROM ((Client_Table INNER JOIN [EQ-TETRA_ISSI] ON Client_Table.[External Customer Index] = [EQ-TETRA_ISSI].[Customer Index])" & _
" INNER JOIN SERVICEREQUEST_TABLE ON Client_Table.[External Customer Index] = SERVICEREQUEST_TABLE.Index) INNER JOIN ([EQ-TETRA_TEI]" & _
" INNER JOIN [EQ-TETRA_REQFUL] ON [EQ-TETRA_TEI].TEI = [EQ-TETRA_REQFUL].TEI) ON (SERVICEREQUEST_TABLE.REQNUM = [EQ-TETRA_REQFUL].Reqnum)" & _
" AND ([EQ-TETRA_ISSI].ISSI = [EQ-TETRA_REQFUL].ISSI)" & _
" WHERE [EQ-TETRA_TEI].TEI=[EQ-TETRA_REQFUL].[TEI] " _
& " AND [EQ-TETRA_ISSI].Active=True " _
& " AND [EQ-TETRA_TEI].Activation=True "
End If
strSQL=strSQL & " ORDER BY [EQ-TETRA_ISSI].ISSI;"
I'm also not convinced that you need to have [EQ-TETRA_TEI].TEI=[EQ-TETRA_REQFUL].[TEI] in the WHERE clause, as you are already using it to join two tables together.
Regards,

SQL Query works in MS-Access but cannot implement it in vb.net form

The block of code immediately following (given to me by a stackoverflow solver) works perfectly in MS-Access. I'm trying to convert it to work in a vb.net form accessing the very same MS-Access database. I get an error and cannot see my mistake. Are there any vb.net coders that can see what I'm doing wrong. The first block of code works in MS-Access and is the code I'm trying to convert. And the second block of code is my conversion attempt.
SELECT at.animalID, amt.milestoneType
FROM
animals_Table at
LEFT JOIN
(
SELECT animalID, milestoneType
FROM animalMilestones_Table
WHERE milestoneType = 'Intake'
) amt
ON at.animalID = amt.animalID
Now, my conversion attempt:
dim selectAnimal as string
selectAnimal = "SELECT at.animalID, amt.milestoneType" & _
" FROM animals_Table at" & _
" LEFT JOIN" & _
" (" & _
" SELECT animalID, milestoneType" & _
" FROM animalMilestones_Table" & _
" WHERE milestoneType = '" & "Intake" & "'" & _
" ) amt" & _
" ON at.animalID = amt.animalID"
The error code I get is
!ErrorInfo.GetDescription failed with E_FAIL(0x80004005)
It appears that ACE.OLEDB doesn't like at as a table alias. Try this instead
Dim selectAnimal As String
selectAnimal = "SELECT atbl.animalID, amtbl.milestoneType" & _
" FROM animals_Table atbl" & _
" LEFT JOIN" & _
" (" & _
" SELECT animalID, milestoneType" & _
" FROM animalMilestones_Table" & _
" WHERE milestoneType = '" & "Intake" & "'" & _
" ) AS amtbl" & _
" ON atbl.animalID = amtbl.animalID"

VBA trouble with SQL

I'm using this code for an Access 2010 database, and I seem to have a problem with the syntax of my SQL statement, but I can't for the life of me figure out how to correctly format the statement. Thank you in advance for any help!
qdf = db.CreateQueryDef("Company_State_Q")
Dim strSQLSearch As String
strSQLSearch = "SELECT [Company Information].Company_Name, " & _
"[Company Information].Industry" & _
"FROM Company Information" & _
"WHERE [Company Information].State ='" & stateV & "'" & _
"ORDER BY [Company Information].Company_Name;"
qdf.SQL = strSQLSearch
You should use ...
Debug.Print strSQLSearch
... in your code after you build the strSQLSearch string. You can then view the string in the Immediate window (go there with Ctrl+g).
From the Immediate window I set the value of stateV to "CA" and tested your string like this:
stateV = "CA" : ? "SELECT [Company Information].Company_Name, " & _
"[Company Information].Industry" & _
"FROM Company Information" & _
"WHERE [Company Information].State ='" & stateV & "'" & _
"ORDER BY [Company Information].Company_Name;"
SELECT [Company Information].Company_Name, [Company Information].IndustryFROM Company InformationWHERE [Company Information].State ='CA'ORDER BY [Company Information].Company_Name;
Once you actually see the completed strSQLSearch string, it's easy to spot multiple problems:
IndustryFROM should be Industry FROM
FROM Company Information should be FROM [Company Information]
InformationWHERE should be Information WHERE
'CA'ORDER should be 'CA' ORDER
Consider using line breaks between the sections of your SELECT statement. The db engine is perfectly happy with line breaks instead of spaces. And you may find it easier to read the statement as several short lines rather than as one long line.
Dim strSQLSearch As String
strSQLSearch = "SELECT ci.Company_Name, ci.Industry" & vbCrLf & _
"FROM [Company Information] AS ci" & vbCrLf & _
"WHERE ci.State ='" & stateV & "'" & vbCrLf & _
"ORDER BY ci.Company_Name;"
Debug.Print strSQLSearch
Also at the beginning of your code you have ...
qdf = db.CreateQueryDef("Company_State_Q")
It seems qdf must be a DAO.QueryDef object, so I think you should use the Set keyword to assign to it.
Set qdf = db.CreateQueryDef("Company_State_Q")

Running a parameter query in Access VBA

I am trying to run the following query in a VBA function. I keep getting "Too few parameters. Expected 1."
strSQL = "Parameters [Report Date] DateTime;" & vbCrLf & _
"SELECT SCF.code AS [Stock Code], " & vbCrLf & _
"SCF.desc AS [Description], " & vbCrLf & _
"SCF.grp AS [Product Group]," & vbCrLf & _
"SCF.qCurr AS [Closing Stock], " & vbCrLf & _
"SCF.abp AS [Avg Price], " & vbCrLf & _
"Sum(([Closing Stock]*[Avg Price])) AS [STOCK VALUE], " & vbCrLf & _
"MaxDate.tDate AS [Last Transaction Date], " & vbCrLf & _
"Sum(IIf(([Last Transaction Date]>[Report Date]),([Closing Stock]*[Avg Price]),0)) AS [After Report Date], " & vbCrLf & _
"DateDiff(""d"",[Last Transaction Date],[Report Date]) AS [Days since Last Transaction], " & vbCrLf & _
"[Report Date]" & vbCrLf & _
"INTO [FinReport] " & vbCrLf & _
"FROM SCF RIGHT JOIN MaxDate ON MaxDate.parent = SCF.this "
strSQL = strSQL & _
"WHERE (SCF.qCurr <> 0) " & vbCrLf & _
"GROUP BY SCF.code, " & vbCrLf & _
"SCF.desc, " & vbCrLf & _
"SCF.grp, " & vbCrLf & _
"SCF.qCurr, " & vbCrLf & _
"SCF.abp, " & vbCrLf & _
"MaxDate.tDate" & vbCrLf & _
"ORDER BY MaxDate.tDate;"
Set qdf = db.CreateQueryDef("", strSQL)
qdf.Parameters("[Report Date]").Value = Form_IO_Form.ReportDate_TB.Value
qdf.Execute
I have verified that all fields (other than [Report Date] of course) exist and the query runs by itself as an access query (pop up asks for [Report Date]).
Help!
Edit 1:
As requested here is the DB file as a ZIP. It is an Access 2007 .accdb file
DB File
Your SQL statement will trigger error #3122 from the db engine:
You tried to execute a query that does not include the specified expression 'DateDiff("d",[Last Transaction Date],[Report Date])' as part of an aggregate function.
That error will cause the statement to fail before the db engine even considers any parameters.
When you build a SQL statement with VBA, it's better to start with one the db engine will accept. Then you should also follow the sound advice from #mwolfe02 to Debug.Print strSQL ... to give yourself an opportunity to examine the completed statement you're asking the db engine to execute.
Edit: Having examined the ACCDB file you uploaded, I still don't understand why your query doesn't trigger error #3122. However the query does work as a saved query and can work when you execute it from VBA code. The reason you got the complaint about "too few parameters" is that you weren't actually executing the temporary QueryDef you created. Instead you were attempting to execute the SQL text like this:
' Execute created Query '
CurrentDb.Execute strSQL, dbFailOnError
If you change to this approach (as you indicated in your question), it works without error:
qdf.Execute
I am guessing that you have a typo in one of your field names. The easiest way to find it is to throw a Debug.Print strSQL line immediately before your Set qdf... line.
Then create a new query in the Access UI, switch to SQL view, paste in the SQL text from the immediate window, and execute the query. Access will prompt you for the Report Date (which you are expecting) and the mistyped name of one of your fields.