Access Error 3141 - vba

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()

Related

How do you UPDATE SQL Table from Excel Table using VBA JOIN

I've read through postStackoverflow 14814098 and would like to know (2) things.
Can you update MS SQL Tables from Excel by creating a string with an Update statement that refers to an Excel Table. Below is a rough idea in VBA of what I mean.
If you add the SQL Statement to the server, how do you call it from Excel using VBA?
Background: I'm attempting to pull a table from the MS SQL Server, Load results into Excel Sheet as an Excel Table where I can exit the sheet and Update all changes back to the server table.
I set up a class and worksheet module to update the server after individual cells are changed in the worksheet, but now I would like to update all the changes at once.
Is there a better way to go about getting the result?
Sub UpdateSqlWithExcelTableJoin()
Dim cmd As ADODB.Command
Dim strSQL As String
Set cnn = New ADODB.Connection
cnn.ConnectionString = "DRIVER=SQL Server;SERVER=MYSERVERNAME;DATABASE=MYDATABASENAME;Trusted_Connection=Yes"
Set cmd = New ADODB.Command
cnn.Open
cmd.ActiveConnection = cnn
cmd.CommandType = adCmdText
Call setString2
cmd.CommandText = strSQLUpdate
cmd.Execute
cnn.Close
Set cmd = Nothing
Set cnn = Nothing
End Sub
Sub setString2()
strSQLUpdate = _
"Update test.profile " & vbNewLine & _
"Set test.profile.Field = ExcelTable.Field " & vbNewLine & _
" test.profile.Profile_Name = ExcelTable.Profile_Name " & vbNewLine & _
"From test.profile " & vbNewLine & _
"INNER JOIN OPENROWSET('MICROSOFT.JET.OLEDB.4.0', 'Excel 8.0;Database=C:\Users\USERNAME\ONEDRIVE - FOLDER\SQL_VBA_b.xlsm;', 'Select ID, Profile_Name' " & vbNewLine & _
"From '[Sheet3$]') As ExcelTable " & vbNewLine & _
"ON test.profile.ID = ExcelTable.ID " & vbNewLine & _
"WHERE (test.profile.ID = ExcelTable.ID " & vbNewLine & _
" AND test.profile.Profile_Name = ExcelTable.Profile_Name)"
Debug.Print strSQLUpdate
End Sub
I always found this to be easier to run this through an MS-Access connection than to connect directly to SQL Server.
Set up an Acess data base with two ODBC connections. A. Define the Excel data as a table to Access. B. Define the SQL Server table as a table to Access.
In Excel VBA change your ADOdb connection to connect to the Access db.
Now you can run a single update statement in ODBC SQL that looks like this:
Update SQLServerTable
Set SQLServerColumn = ExcelColumn
From SQLServerTable S
Inner Join ExcelTable E
Where sqlServerkey = ExcelKey
This is more flexible than trying to do it directly because if the update relationship grows more complex you can always code the FROM clause as an Access saved query, and that is the only good way to do nested queries in ODBC (queries that use other queries in FROM).
You can't use select at that position it will always take the whole sheet.
Also you are missing a comma after Field
Update test.profile
Set test.profile.Field = ExcelTable.Field,
test.profile.Profile_Name = ExcelTable.Profile_Name
From test.profile
INNER JOIN OPENROWSET('MICROSOFT.JET.OLEDB.4.0', 'Excel 8.0;Database=C:\Users\USERNAME\ONEDRIVE - FOLDER\SQL_VBA_b.xlsm;', [Sheet3$]) As ExcelTable
ON test.profile.ID = ExcelTable.ID
WHERE (test.profile.ID = ExcelTable.ID
AND test.profile.Profile_Name = ExcelTable.Profile_Name)
ado to retrieve records from SQL
save excel file
add excel file and SQL Table in Access
create Update Query using linked tables (sql and excel)
Open workbook / update data / close workbook / run query.

How can I combine these two SQL queries (Access/VBA)

I am using two SQL queries in VBA that i believe they could be done in one, but I cant get it to work. I Want to turn the VBA portion into a Query outside of VBA, the VBA keeps breaking my file due to the amount of data it processes. (By break i mean it gives a message that says "this file is not a valid database" rendering the file corrupted). I search for that error but all i found was not related to breaking because of VBA code.
Anyways, here are the two queries ran with VBA.
SELECT ET.VerintEID AS EID, Sum(ET.ExceptMin)/60 AS Exeptions
FROM Tbl_VExceptTime AS ET
INNER JOIN Tbl_VCodes ON ET.Exception = Tbl_VCodes.Exception
WHERE (ET.ExceptDate Between #" & sDate & "# And #" & eDate & "#)
GROUP BY ET.VerintEID, Tbl_VCodes.IsApd
HAVING Tbl_VCodes.IsApd = ""OFF"";
I loop these results to update a table.
Do While Not .EOF
SQL = "UPDATE Tbl_AttendanceByAgent SET EXC = " & recSet.Fields(1).Value & _
" WHERE VerintID = '" & recSet.Fields(0).Value & "'"
CurrentDb.Execute SQL
.MoveNext
Loop
I know that i can save the results from the first query into a table and without looping I can update the main table with another SQL query, but I believe it can be done on a single SQL. I have tried using an UPDATE with a SELECT of the first query but it just errors out on me with an invalid syntax.
Yes this could be achieved in one single query as shown below
UPDATE Tbl_AttendanceByAgent
SET Tbl_AttendanceByAgent.EXC = t2.Exeptions
from Tbl_AttendanceByAgent t1
inner join (
SELECT ET.VerintEID AS EID, Sum(ET.ExceptMin)/60 AS Exeptions
FROM Tbl_VExceptTime AS ET
INNER JOIN Tbl_VCodes as TV ON ET.Exception = TV.Exception
WHERE (ET.ExceptDate Between #" & sDate & "# And #" & eDate & "#)
GROUP BY ET.VerintEID, TV.IsApd
HAVING Tbl_VCodes.IsApd = 'OFF'
) AS t2 on t2.EID = t1.VerintID
Note: I suppose you will replace sDate, eDate with values within your code
This question is an answer to the described errors and the given code, although it technically does not answer the request for a single SQL statement. I started adding a comment, but that's just too tedious when this answer box allows everything to be expressed efficiently at once.
First of all, referring to CurrentDb is actually NOT a basic reference to a single object instance. Rather it is more like a function call that generates a new, unique "clone" of the underlying database object. Calling it over and over again is known to produce memory leaks, and at the least is very inefficient. See MS docs for details.
Although the given code is short, it's not sweet. Not only is it repeatedly creating new database objects, it is repeatedly executing an SQL statement to update what I assume is a single row each time. That also entails regenerating the SQL string each time.
Even if executing the SQL statement repeatedly was an efficient option, there are better ways to do that, like creating a temporary (in-memory) QueryDef object with parameters. Each loop iteration then just resets the parameters and executes the same prepared SQL statement.
But in this case, it may actually be more efficient to load the table being updated into a DAO.Recordset, then use the in-memory Recordset to search for a match, then use the recordset to update the row.
I suspect that addressing a couple of those issues would make your VBA code viable.
Dim db as Database
Set db = CurrentDb 'Get just a single instance and reuse
Dim qry as QueryDef
SQL = "PARAMETERS pEXC Text ( 255 ), pID Long; " & _
" UPDATE Tbl_AttendanceByAgent SET EXC = pEXC " & _
" WHERE VerintID = pID"
set qry = db.CreateQueryDef("", SQL)
'With recSet '???
Do While Not .EOF
qry.Parameters("pEXC") = recSet.Fields(1).Value
qry.Parameters("pID") = recSet.Fields(0).Value
qry.Execute
.MoveNext
Loop
'End With recSet '???
'OR an alternative
Dim recUpdate As DAO.Recordset2
Set recUpdate = db.OpenRecordset("Tbl_AttendanceByAgent", DB_OPEN_TABLE)
Do While Not .EOF
recUpdate.FindFirst "VerintID = " & recSet.Fields(0).Value
If Not recUpdate.NoMatch Then
recUpdate.Edit
recUpdate.Fields("EXC") = recSet.Fields(1).Value
recUpdate.Update
End If
.MoveNext
Loop
I realized in commenting on Gro's answer, that the original query's aggregate clauses will produce unique values on EID, but it then becomes obvious that there is no need to group on (and sum) values which do not have Tbl_VCodes.IsApd = 'OFF'. The query would be more efficient like
SELECT ET.VerintEID AS EID, Sum(ET.ExceptMin)/60 AS Exeptions
FROM Tbl_VExceptTime AS ET
INNER JOIN Tbl_VCodes ON ET.Exception = Tbl_VCodes.Exception
WHERE (ET.ExceptDate Between #" & sDate & "# And #" & eDate & "#)
AND Tbl_VCodes.IsApd = 'OFF'
GROUP BY ET.VerintEID;
BTW, you could consider implementing the same temporary QueryDef pattern as I showed above, then you'd change the first WHERE expression to something like
PARAMETERS PsDate DateTime, PeDate DateTime;
...
WHERE (ET.ExceptDate Between [PsDate] And [PeDate])
...

MS Access SQL query within VBA code

I need a second set of eyes on this SQL query embedded in the VBA code. I'm making an MS Access app that returns a dataset based on the to & from date criteria set by the user in the specific date picker boxes. The sql query that you see actually has been tested statically within MS Access query design view. I tested it with actual dates where you see the Me.from_filter and Me.to_filter. It worked perfectly! If you chose something like from 1/1/2015 to 5/1/2015 it returns columns of all the months you need. Perfect. Now when I embed it in the VBA code and assign it to a control variable, I get the "Run-time error '2342': A RunSQL action requires an argument consisting of an SQL statement." Can someone please eyeball this and tell me what might be wrong?
Option Compare Database
Option Explicit
Dim strSQL As String
Private Sub Command0_Click()
If IsNull(Me.from_filter) Or IsNull(Me.to_filter) Then
MsgBox "You have not entered a start date or end date"
Else
strSQL = "TRANSFORM Sum(dbo_ASSET_HISTORY.MARKET_VALUE) AS SumOfMARKET_VALUE " _
& "SELECT [dbo_FIRM]![NAME] AS [FIRM NAME], dbo_FUND.CUSIP, dbo_FUND.FUND_NAME, dbo_FUND.PRODUCT_NAME " _
& "FROM (dbo_ASSET_HISTORY INNER JOIN dbo_FIRM ON dbo_ASSET_HISTORY.FIRM_ID = dbo_FIRM.FIRM_ID) INNER JOIN dbo_FUND ON dbo_ASSET_HISTORY.FUND = dbo_FUND.FUND " _
& "WHERE (((dbo_FIRM.Name) Like 'Voya F*') And ((dbo_ASSET_HISTORY.PROCESS_DATE) >= #" & Me.from_filter & "# And (dbo_ASSET_HISTORY.PROCESS_DATE) <= #" & Me.to_filter & "#)) " _
& "GROUP BY [dbo_FIRM]![NAME], dbo_FUND.CUSIP, dbo_FUND.FUND_NAME, dbo_FUND.PRODUCT_NAME " _
& "PIVOT [dbo_ASSET_HISTORY]![ASSET_YEAR] & '-' & [dbo_ASSET_HISTORY]![ASSET_MONTH];"
DoCmd.RunSQL (strSQL)
End If
End Sub
RunSQL is for running action queries like update, insert, select into, delete, etc as per Microsoft definition https://msdn.microsoft.com/en-us/library/office/ff194626.aspx , you should probably use OpenQuery or similar to run your query.

How to run parameterized query from VBA. Parameters sourced from recordset

I have a form where a user selects a vendor's name from a combobox, whose catalog file is to be imported. The combobox selection then drives a query to create a one-record recordset (rsProfile) containing several profile variables queried from a table of all vendor profiles. These variables are then used in a series of different queries to reformat, translate and normalize the vendor's uniquely structured files to a standardized format that can be imported into our system.
I am frustrated that I can't figure out how to build my stored queries that will use one or more parameters that are automatically populated from the profile recordset.
Here is my rsProfile harvesting code. It works. Note that intVdrProfileID is a global variable set and used in other places.
Private Sub btn_Process_Click()
Dim ws As Workspace
Dim db, dbBkp As DAO.Database
Dim qdf As DAO.QueryDef
Dim rsProfile, rsSubscrip As Recordset
Dim strSQL As String
Dim strBkpDBName As String
Dim strBkpDBFullName As String
strBkpDBName = Left(strVdrImportFileName, InStr(strVdrImportFileName, ".") - 1) & "BkpDB.mdb"
strBkpDBFullName = strBkpFilePath & "\" & strBkpDBName
Set db = CurrentDb
Set ws = DBEngine.Workspaces(0)
MsgBox ("Vendor Profile ID = " & intVdrProfileID & vbCrLf & vbCrLf & "Backup file path: " & strBkpFilePath)
' Harvest Vendor Profile fields used in this sub
strSQL = "SELECT VendorID, Div, VPNPrefix, ImportTemplate, " & _
"VenSrcID, VenClaID, ProTyp, ProSeq, ProOrdPkg, ProOrdPkgTyp, JdeSRP4Code, " & _
"PriceMeth, " & _
"ProCost1Frml, ProCost2Frml, " & _
"ProAmt1Frml, ProAmt2Frml, ProAmt3Frml, ProAmt4Frml, ProAmt5Frml " & _
"FROM tZ100_VendorProfiles " & _
"WHERE VendorID = " & intVdrProfileID & ";"
Set qdf = db.QueryDefs("qZ140_GetProfileProcessParms")
qdf.SQL = strSQL
Set rsProfile = qdf.OpenRecordset(dbOpenSnapshot)
DoCmd.OpenQuery "qZ140_GetProfileProcessParms"
' MsgBox (qdf.SQL)
I have used QueryDefs to rewrite stored queries at runtime, and although it works, it is quite cumbersome and does not work for everything.
I was hoping for something like the sample below as a stored query using DLookups. I can get this to work in VBA, but I can't get anything to work with stored queries. I am open to other suggestions.
Stored Query "qP0060c_DirectImportTape":
SELECT
DLookUp("[VPNPrefix]","rsProfile","[VendorID]=" & intVdrProfileID) & [PartNo] AS VenPrtId,
Description AS Des,
DLookup("[Jobber]","rsProfile",[VendorID=" & intVdrProfileID) AS Amt1,
INTO tP006_DirectImportTape
FROM tJ000_VendorFileIn;
ADDENDUM:
Let me adjust the problem to make it a bit more complex. I have a collection of about 40 queries each of which use a different collection of parameters (or none). I also have a table containing the particular set of queries that each vendor 'subscribes' to. The goal is to have a database where a non-coding user can add new vendor profiles and create/modify the particular set of queries which would be run against that vendor file. I have almost 100 vendors so far, so coding every vendor seperately is not practical. Each vendor file will be subjected to an average of 14 different update queries.
Simplified Example:
Vendor1 file needs to be processed with queries 1, 2 and 5. Vendor2 file might need only update queries 2 and 4. The parameters for these queries might be as follows:
query1 (parm1)
query2 (parm1, parm4, parm8, parm11)
query4 (parm5, parm6, parm7, parm8, parm9, parm10, parm11)
query5 () -no parms required
This is the core query processing that loops through only the queries relevant to the current vendor file. rsSubscrip is the recordset (queried from a master table) containing this filtered list of queries.
' Run all subscribed queries
MsgBox "Ready to process query subscription list."
With rsSubscrip
Do While Not .EOF
db.Execute !QueryName, dbFailOnError
.MoveNext
Loop
.Close
End With
You can set the parameters of a predefined query using the syntax;
Set qdf = CurrentDB.QueryDefs(QueryName)
qdf.Parameters(ParameterName) = MyValue
To add parameters to the query, add the following before the SELECT statement in the sql
PARAMETERS [ParameterOne] DataType, [ParameterTwo] DataType;
SELECT * FROM tblTest;

Join operation syntax error

I am trying to make a query through a button; though it says
Syntax error in JOIN operation. When I click it... I can't find the syntax error, maybe you guys can help me out? This is the code:
Set qdef = CurrentDb.CreateQueryDef("UnitMoreInfoQ", _
"SELECT UnitsT.*, WorkOrdersQ.CustomerName, WorkOrdersQ.ClientName, WorkOrdersQ.WorkOrderNumber " & _
"FROM UnitsT inner join workordersQ on WorkOrdersT.WorkOrerID=WorkOrdersQ.WorkOrderID " & _
"WHERE UnitsT.UnitID = " & txtWorkOrderID.Value)
The problem seems to be with your JOIN condition:
WorkOrdersT.WorkOrerID=WorkOrdersQ.WorkOrderID
There isn't a WorkOrdersT table or table alias defined in the FROM or other join, so your query isn't valid.
Make it easier on yourself to find and fix SQL errors.
Use a string variable to hold the SQL statement your code constructs. You can Debug.Print that statement before using it with CreateQueryDef. Then when troubleshooting, go to the Immediate window (Ctrl+g) to examine the statement your code is attempting to use. You can copy it from there and then paste it into SQL View of a new query for further testing. And if you need help, show us the completed statement text instead of the VBA code which builds the statement.
Dim strSelect As String
strSelect = "SELECT u.*, w.CustomerName, w.ClientName, w.WorkOrderNumber " & _
"FROM UnitsT As u inner join workordersQ AS w " & _
"on u.WorkOrerID=w.WorkOrderID " & _
"WHERE u.UnitID = " & txtWorkOrderID.Value
Debug.Print strSelect
Set qdef = CurrentDb.CreateQueryDef("UnitMoreInfoQ", strSelect)
Alternatively, use the query designer to create the query you need from scratch. Once you get that working, switch to SQL view, copy the statement text, and revise your VBA code to create the same statement.