Can I run CurrentDb.Execute from MS Access with an SQL Server Query in it? - vba

I have 2 tables, one of them as been imported from an Excel file and therefore i created the 2nd one in order to gather some info from the imported and generate in the end a new Excel Sheet to be imported in another place.
My question here is:
I have table a with ID's, names, account status, etc.
The second table has the ID's imported from table a, and now i want to generate a commentary (a value with the concatenation from the several columns in table a) like this:
DATE: 20/12/2017 | FirstName LastName | ID: 123456
For this i prepared by table b with the ID's already and my code in MS ACCESS VBA at the moment is:
CurrentDb.Execute "UPDATE a " _
& " SET a.Commentary = 'Date: ' + CONVERT(nvarchar,GETDATE(),103) + ' | FirstName LastName | ID: ' + b.ID " _
& " FROM tableA a " _
& " INNER JOIN tableB b " _
& " ON a.IdNum = b.ID"
I don't know why this gives me the error of:
Run-time error '3075': Syntax error (missing operator) in query
expression
....(the above code)
Tried to modify the code to more VBA language, more MS-Access language but with no luck.
Thank you,
Diogo

Your syntax looks like T-SQL, if you're going to use CurrentDb.Execute, you need to use Jet/ACE SQL. That means: specifying ALL tables directly after UPDATE, no CONVERT, no GETDATE(), and & as the preferred concatenation operator.
CurrentDb.Execute "UPDATE tableA a INNER JOIN tableB b ON a.IdNum = b.ID" _
& " SET a.Commentary = 'Date: ' & Date() & ' | FirstName LastName | ID: ' & b.ID "
Note that I assume you're using linked tables, since I don't see a schema specified anywhere.

No. You will need a connection string and a saved or created pass-through query that uses this connection.
Then set the SQL property of this query to your SQL string and execute the query.
Or - if you have the tables linked - do as Erik suggests.

You could also use an ADODB command to pass the query directly to the SQL server.
You'd want to add some error handling, but something like this will work as a starting point:
Public Sub ExecuteStatementOnSqlServer(sql As String)
Dim cmd As ADODB.Command
Set cmd = New ADODB.Command
With cmd
.ActiveConnection = bf_sqlServerConnection
.CommandType = adCmdText
.CommandText = sql
.Execute
End With
Set cmd = Nothing
End Sub

Related

MS Access SQL - Update field in one table with a count from another table

I have a table called 'FilesUploaded' which has a summary of all files uploaded to my access DB. I want to add a field in here that contains the count of all errors from another table.
My FilesUploaded table contains a field called 'FileName' which has
the full name of the file.
I want to get a count of all records in table1 where the 'ValidityCheck' field contains 'Error'. Table1 also contains a field called 'Name_of_Report' which has the file name which will match back to the FilesUploaded table.
The 'vFileName' variable will contain what is in both the 'Filename' field and the 'Name_of_Report' field
The below is the code I have tried using, but it says this type of join is not allowed and I have no idea what other way I can achieve this.
Call RunSQL("UPDATE FilesUploaded " & _
"LEFT JOIN (SELECT table1.Name_of_Report, Sum(IIf([table1].[ValidityCheck] Like '*Error*',1,0)) AS ErrorCount FROM table1 GROUP BY table1.Name_of_Report) AS temp on temp.Name_of_Report = FilesUploaded.FileName " & _
"SET " & _
"FilesUploaded.[ErrorCount] = temp.ErrorCount " & _
"WHERE FilesUploaded.[FileName] = '" & vFileName & "' ")
Does anybody know a different way can update the FilesUploaded table with a count of the ValidityCheck field from the Table1 table?
In MS Access, UPDATE...JOIN requires its analogous SELECT...JOIN to be updateable. Aggregate queries using SUM are not updateable queries. Therefore, consider domain functions like DSum.
Additionally, consider a stored query and call it in VBA with parameterization via QueryDefs. Do note the use of ALIKE to use % for wildcards in case you need to run query outside of the MS Access GUI such as in ODBC or OLEDB connections where * is not recognized.
SQL (save as a stored query)
PARAMETERS paramFileName TEXT;
UPDATE FilesUploaded f
SET f.[ErrorCount] = DSUM("*", "table1",
"[ValidityCheck] ALIKE '%Error%' AND [Name_of_Report]='" & f.[FileName] & "'")
WHERE f.[FileName] = [paramFileName];
VBA (run query without string concatenation)
Dim qdef As QueryDef
Set qdef = CurrentDb.QueryDefs("mySavedQuery")
qdef![paramFileName] = vFileName ' BIND PARAM VALUE
qdef.Execute ' RUN ACTION QUERY
Set qdef = Nothing

Outputting the result of an SQL query into a text box

I'm creating a form that displays information about an individual that is taken from two tables when a name is entered by the user. I want the fields to be output into they're own text boxes. My code looks similar to what's below.
When I run the code it displays the literal query "SELECT name etc..." in the textbox. I saw Dlookup works for textboxes but to my understanding it doesn't work well with more than one table. Any advice would be greatly appreciated!
PS I'm a VBA/access newbie
Dim SQL, SearchInput As String
SQL = "SELECT name" & _
"FROM tablename INNER JOIN othertablename ON tablename.name = othertablename.name" & _
"WHERE tablename.name LIKE ""*" & SearchInput & "*""
Me.txtbox = SQL
I'm pretty sure this is a duplicate, but it's faster to answer than hunt for the other posts.
You need to declare a recordset and assign the data returned from the select statement to it. This will leave you with something very similar to an array. After that you need to just line up the array element to the positions of the columns. IE. rs(0)=name in the select statement above.
Dim rs As Recordset
Dim SQL As String, SearchInput As String
SQL = "SELECT name " & _
"FROM tablename INNER JOIN othertablename ON tablename.name = othertablename.name " & _
"WHERE tablename.name LIKE ""*" & SearchInput & "*""
Set rs = CurrentDb.OpenRecordset(strSQL, dbOpenSnapshot)
Me.txtBox = rs(0)
That should work.
Since you are new to MS Access & VBA in general, I'll propose a solution which avoids VBA entirely.
In your current code, I assume that SearchInput is sourcing its value from another control on your form.
I would suggest creating a new saved query (called it MyQuery) with the following SQL:
select table2.name from table1 inner join table2 on table1.name = table2.name
Then, in the Control Source for your textbox, use a DLookup expression with the following arguments:
=dlookup("[name]", "MyQuery", "table1.name like '*" & [SearchInput] & "*'")
Here, [SearchInput] refers to the name of the control on your form containing the search criteria.

Run one MS Access SQL script on a particular Table chosen by user

I have a MS Access 2016 database (*.accdb) with 20+ Tables. Fields in each of them vary slightly from Table to Table. I've no VBA experience, so I'm sticking only to the SQL query below (redacted).
SQL script
myvar below is the parameter I'd like to be prompted when the script is run so that I enter the Table I want the changes applied to.
PARAMETERS
[myvar] TableID;
UPDATE
[myvar]
INNER JOIN
Excel_Data ON [myvar].[Part Number] = Excel_Data.[Part Number]
SET
[myvar].[Value] = '?',
[myvar].Description = Excel_Data.Description,
[myvar].[Ref] = '?'
.
.
.
WHERE
[myvar].Description Is Null;
Output
Error message:
Too few parameters. Expected 0.
What I need
I prefer a solution for above in a SQL script form as above, not involving VBA, preferably. I'd like to enter the Table name when prompted so the script knows which table to UPDATE. FYI: The PARAMETERS work when it is not a Table as I've shown in my script above.
Help/advise is highly appreciated.
EDIT 1
Since it seems not possible to use parameters as Table names, could you suggest a VBA solution? A sample code, perhaps?
As said in the comments, you can't really solve this without VBA.
You can store your SQL query in a string, and use a placeholder to indicate the tablename. Then get the tablename using an inputbox and replace the placeholder with the tablename.
Dim sqlString As String
sqlString = "UPDATE [%Placeholder%] " & vbCrLf & _
"INNER JOIN Excel_Data ON [%Placeholder%].[Part Number] = Excel_Data.[Part Number] " & vbCrLf & _
"SET [%Placeholder%].[Value] = '?', " & vbCrLf & _
...
"WHERE [%Placeholder%].Description Is Null;"
sqlString = Replace(sqlString, "%PlaceHolder%", InputBox("Enter a tablename"))
CurrentDb.Execute sqlString
In a more mature solution, I'd create a form with a combobox containing all available table names, and add a function to sanitize tablenames (replace "]" with "]]")

Access Error 3141

I am trying to set recordset for a report using the following query:
Dim RS As Recordset
Set RS = CurrentDb.OpenRecordset("Select DISTINCT SalesOrders.SalesOrderNumber, Vendors.Name, SalesOrders.OrderDate, SalesOrders.Grade, SalesOrders.QuantityUOM, SalesOrders.PortOfDischarge, SalesOrders.Quantity, IIf([SalesOrders.DTHCIncludedYN],'DTHC INCLUDED','DTHC NOT INCLUDED') AS DTHCIncludedYN," & _
" SalesOrders.DeliveryTerms, SalesOrders.SalesOrderID, SalesOrders.GenesisDocumenationAssistant, Products.ProductLoadPorts, Customers.CustomerType, SalesOrders.UnitPriceUOM, SalesOrders.UnitPrice, Customers.CustomerName, Products.ProductName, SalesOrders.PaymentTerms, SalesOrders.PlaceOfDelivery, SalesOrders.SalesCommission, SalesOrders.LatestShipDate, [SalesOrders.Quantity]*[UnitPrice] AS Amount," & _
" IIf([AdvisingBank]='GEB','GREAT EASTERN BANK',IIf([AdvisingBank]='BOC','BANK OF CHINA',IIf([AdvisingBank]='CB','CATHAY BANK',IIf([AdvisingBank]='HSBC','HSBC Bank USA',IIf([AdvisingBank]='COM','COMMERCE BANK'))))) AS [Bank Name]," & _
" IIf([CCICType]= '1','ONE ORIGINAL INSPECTION CERTIFICATE ISSUED BY CCIC NORTH AMERICA INC','ONE ORIGINAL PRESHIPMENT INSPECTION CERTIFICATE ISSUED BY ANY CCIC EUROPEAN OFFICE') AS [CCIC-Clause]," & _
" IIf([OnCarriageIncluded],'ON CARRIAGE INCLUDED','ON CARRIAGE NOT INCLUDED') AS OCIText, IIf(IsNull([PlaceOfDelivery]),[PortOfDischarge],[PlaceOfDelivery]) AS PODText, Vendors.AB1AddressLine1, Vendors.SupplierLocation AS [Swift Code], " & _
" IIf(IsNull(AdvisingBank),' ','TEL ' & [Vendors.AB1Phone] & ', ' & 'FAX ' & [Vendors.AB1Fax]) AS [Contact Details], IIf(IsNull(AdvisingBank),'',Vendors.AB1AddressLine1 & ', ' & [Vendors.AB1City] & ', ' & [Vendors.AB1State] & ' ' & [Vendors.AB1Zip] & ' ' & [Vendors.AB1Country]) AS AddressLine," & _
" FROM (Products INNER JOIN (Customers INNER JOIN SalesOrders ON Customers.CustomerID = SalesOrders.CustomerID) ON Products.Grade = SalesOrders.Grade) LEFT JOIN Vendors ON SalesOrders.AdvisingBank = Vendors.VendorID " & _
" WHERE (SalesOrders.SalesOrderNumber= Forms!frmPrintContracts!txtGreenSales AND ((Customers.CustomerType)='GREEN' Or (Customers.CustomerType)='GREEN-JC' Or (Customers.CustomerType)='GREEN-DL' Or (Customers.CustomerType)='SIHU' Or (Customers.CustomerType)='PAPYRUS'))ORDER BY SalesOrders.SalesOrderNumber DESC ")
I am getting error saying that I have used reserved keyword or there is a punctuation mistake. Can anyone help me figure out what the error is.
Any help is appreciated. Thank you
Consider saving your SQL statement as an Access stored query and not VBA string for the following reasons:
All syntax errors are checked before you save. You cannot save via the MS Access query design GUI a non-compilable query.
Stored Access queries are more efficient than VBA string queries as the database engine saves the best execution plan for stored queries and cannot when called on the fly in VBA.
You can set a saved query to most Access objects (comboboxes or listboxes row sources, form or report recordsources) with less code.
Me.cboText.RowSource = "myStoredQuery"
Me.cboText.RowSourceType = "Table/Query"
Me.cboText.Requery
Me.Form.RecordSource = "myStoredQuery"
Me.Form.Requery
Your application code is more readable and maintainable as you avoid the VBA string concatenation. Plus, you abstract away the special-purpose nature of SQL from your application layer code.
Dim RS As Recordset
Set RS = CurrentDb.OpenRecordset("myStoredQuery")
The industry standard of parameterization is easier to achieve with stored queries which can serve as a prepared statement. If you ever need to pass VBA variable values for dynamic querying, you can parameterize stored queries with PARAMETERS clause and querydefs all while still using stored queries. See example below:
Stored Query
PARAMETERS [myParam] Date;
SELECT DISTINCT SalesOrders.SalesOrderNumber
FROM SalesOrders
WHERE SalesOrders.OrderDate = [myParam]
VBA
Dim qdef As QueryDef
Dim RS As Recordset
Set qdef = Currentdb.QueryDefs("myStoredQuery")
qdef!myParam = Date()
Set RS = qdef.OpenRecordset()

Access VBA Code

Could any expert tell me why I always get this error "Expected:End of Statement" when I run the following code in Access VBA :
FNSQL = DoCmd.RunSQL _
"SELECT First_Name FROM tblPatient_Info WHERE Patient_ID = '" & 1967 & "';"
Thank you so much in advance!
Your code is wrong in a couple different ways.
You don't tell us what FNSQL is, so one can only imagine.
Mistake #1
Mentioned by #simoco, DoCmd.RunSQL is used in the following scenarios:
Here's the API: DoCmd.RunSQL SQLStatement UseTransaction
SQLStatement : A string expression that's a valid SQL statement for an action query or a data-definition query. It uses an INSERT INTO, DELETE, SELECT...INTO, UPDATE, CREATE TABLE, ALTER TABLE, DROP TABLE, CREATE INDEX, or DROP INDEX statement.
Include an IN clause if you want to access another database.
Mistake #2
FNSQL
I'm not sure what you are aiming to do with the results of the query, but this is for your general knowledge because if you didn't know the RunSQL syntax you may be unfamiliar with Recordsets.
You can assign your stored query's results to a Recordset and do with it as you please, much like a table.
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim SQL As String
Dim firstName As String
SQL = "SELECT First_Name FROM tblPatient_Info WHERE Patient_ID = 1967;"
' OR
SQL = "SELECT First_Name FROM tblPatient_Info WHERE Patient_ID = '" & 1967 & "';"
' You can write Debug.Print SQL to make sure you're getting the right SQL.
Set db = CurrentDb
Set rs = db.OpenRecordset(SQL)
' The disadvantage of this approach is that the query string must be compiled each time it runs
' OR
Set rs = db.OpenRecordset(YourSaveQueryName)
'This would be the faster method of the 2.
You can then manipulate your data however you want, and loop through the entire recordset.
firstName = "Larry"
If rs.BOF And rs.EOF Then
' Do nothing, the recordset is empty
Else
Do Until rs.EOF
rs.Edit
rs!FirstName = firstName 'This is how you access properties in the recordset
rs.Update
rs.MoveNext
Loop
etc.
I think you need parentheses around your SELECT statement when you pass it to RunSQL, because it's called as a function that returns "something" that gets assigned to FNSQL. So something like this:
FNSQL = DoCmd.RunSQL( _
"SELECT First_Name FROM tblPatient_Info WHERE Patient_ID = '" & 1967 & "';")
In fact, looking at the RunSQL documentation, RunSQL doesn't return anything so, even if you fix your original issue, you'll still find there's an error there.
Furthermore, again according to the documentation, you can only execute SELECT ... INTO statements with RunSQL. For simple SELECT ... FROM statements, simoco is right -- you need to use a Recordset.
2 errors.
First, DoCmd.RunSQL won't do anything with just a select. Maybe you are missing your action (delete or something) or maybe you want to read it, so you should use CurrentDb.OpenRecordset . You'll need to post more code so we can better understand where you are going with this.
Second, if Patient_ID is an integer (and I'm guessing it is), you don't need the '. You don't need the ; either.
So the query should look like this:
varInt = 1967
"SELECT First_Name FROM tblPatient_Info WHERE Patient_ID = " & varInt
If you don't want to use a var for the int, just insert it directly into the string like so:
"SELECT First_Name FROM tblPatient_Info WHERE Patient_ID = 1967"
In case Patient_ID is a string, you will indeed need ', but you are also missing " around the string, so it should look like this:
"SELECT First_Name FROM tblPatient_Info WHERE Patient_ID = '" & "1967" & "'"
Not a correct answer to this question but it's worth noting I just wanted to see if a row existed in the database. I assumed that would require me to write some sort of select or result query. However access has a separate function just to run count operations.
So this code I ended up with:
Dim count As Long
count = DCount("release_id", "list_releases", "list_id = 1 AND release_id = " & Me!release_id)
If count Then
Me!owned.Enabled = False
MsgBox "You own this release."
End If