Must declare scalar variable when using parameterized sql query - vba

I am trying to access a SQL database using embedded SQL in VBA. The problem is in defining the conditions of what I want to access ( ID > #Identifie is the part causing the problem). When I run the code, I get Must declare the scalar variable #Identifie. However when I go to my locals window, #Identifie is correctly declared and has the value I assigned in cell K6. Moreover, if I replace #Identifie in my condition (ID > #Identifie) by a certain value, my code runs perfectly, thus there are no other errors. It seems that my problem comes from the append method, but I can't figure out what i'm doing wrong.
Here is my code:
(I removed the connection string in this post, but this was clearly not the problem):
Option Explicit
Option Base 1
Sub LoadData()
Application.ScreenUpdating = False
Dim cn As New ADODB.Connection
Dim rs As New ADODB.Recordset
Dim cmd As New ADODB.Command
Dim lastID As Double
Dim parametre As New ADODB.Parameter
Dim Last_total_ID As Double
Last_total_ID = ActiveWorkbook.Worksheets("Consolidated").Range("K6").Value
cn.ConnectionString = ""
cn.Open
Set parametre = cmd.CreateParameter(Name:="#Identifie", Type:=adDouble, Direction:=adParamInput)
parametre.Value = Last_total_ID
cmd.Parameters.Append parametre
cmd.ActiveConnection = cn
cmd.CommandText = "Select ID, Issuer,LaunchDate,SettleDate,CADAmount,Description,Price,Currency, Maturity,IssuerID, Coupon FROM dbo.tblHistoricalIssuanceStats WHERE (IsProvy = 1) AND (ID > #Identifie)"
Set rs = cmd.Execute

Why not:
cmd.CommandText = "Select ID, Issuer,LaunchDate,SettleDate,CADAmount,Description,Price,Currency, Maturity,IssuerID, Coupon FROM dbo.tblHistoricalIssuanceStats WHERE (IsProvy = 1) AND (ID > " & Last_total_ID & ");"
I don't think you need to use an ADODB parameter.

Related

DAO passthrough query using VBA: "Error 3131 Syntax error in from clause"

I copied a solution found in Stack Overflow, adapted it to my needs.
Public Function getAssortmentTypes(Optional personId As Variant) As DAO.Recordset 'personId is integer
Dim strQuery As String
Dim qdf As DAO.QueryDef
Dim rst As DAO.Recordset
If IsMissing(personId) Then
strQuery = "SELECT assortment_type.type_id, assortment_type.type_name AS qryTest FROM assortment_type"
Else
strQuery = "SELECT * FROM get_non_deleted_assortment_types_by_user(" & personId & ")"
End If
Set qdf = CurrentDb.CreateQueryDef("")
With qdf
.SQL = strQuery
.Connect = getDBConnectionString
.ReturnsRecords = True
End With
Set rst = qdf.OpenRecordset
Debug.Print rst!qryTest
Set getAssortmentTypes = rst
End Function
In my postgresql db I do have a working function and appropriate tables. I've tested sql queries with DBEaver and they work.
I'm receiving just one row (should be about 30) when I call the function without a parameter.
With a parameter I expect filtered resultset but receive
"Error 3131 Syntax error in from clause".
Always set the connection string before setting the SQL.
When you set the SQL, DAO doesn't have a clue this will later become a passthrough query, so it tries to parse it as Access SQL, and obviously fails, since it's not valid Access SQL.
Simply change the order:
With qdf
.Connect = getDBConnectionString
.ReturnsRecords = True
.SQL = strQuery
End With
Do note that you should be using parameters, and generally, use ADO instead of DAO when working with external data sources. DAO is great with Access, but offers less features with external data sources. ADO won't try parsing the SQL string before it actually needs to, for example.
Forgo the need for DAO and QueryDefs and use ADO with command parameterization which can then be binded to a recordset:
' SET REFERENCE TO Microsoft ActiveX Data Object #.# Library
Public Function getAssortmentTypes(Optional personId As Variant) As ADODB.Recordset
Dim conn As ADODB.Connection
Dim rst As ADODB.Recordset
Dim cmd As ADODB.Command
Set conn As New ADODB.Connection
conn.Open getDBConnectionString
' PREPARED STATEMENT WITH QMARKS ?
If IsMissing(personId) Then
strQuery = "SELECT assortment_type.type_id, assortment_type.type_name AS qryTest FROM assortment_type"
Else
strQuery = "SELECT * FROM get_non_deleted_assortment_types_by_user(?)"
End If
Set cmd = New ADODB.Command
With cmd
.ActiveConnection = conn
.CommandText = strQuery
.CommandType = adCmdText
' BIND PARAMETER
.Parameters.Append .CreateParameter("user_param", adInteger, adParamInput, , personId)
' EXECUTE QUERY AND BIND INTO RECORDSET
Set rst = .Execute
End With
Set cmd = Nothing
Set getAssortmentTypes = rst
End Function
I don't recommend the introduction of ADO.
The issue looks to be that your first SQL query would (and does) work as a linked table, and thus works because it not a pass-through query.
The 2nd sql fails, since it still try to use "access" sql, and not postgresSQL syntax.
I recommend that you create a PT query (using Access UI). In the designer, make sure you select pass-though:
So, like linked tables - put the connection string in that PT query.
Do not put or attempt to place connection strings in the code. Your re-link routines can thus also include to re-link PT queries.
You can now use this code:
Public Function getAssortmentTypes(Optional personId As Variant) As DAO.Recordset 'personId is integer
Dim rst As DAO.Recordset
Dim strQuery As String
If IsMissing(personId) Then
strQuery = "SELECT assortment_type.type_id, assortment_type.type_name AS qryTest FROM assortment_type"
Else
strQuery = "SELECT * FROM get_non_deleted_assortment_types_by_user(" & personId & ")"
End If
With CurrentDb.QueryDefs("qryPT")
.SQL = strQuery
Set rst = .OpenRecordset
End With
Debug.Print rst!qryTest
Set getAssortmentTypes = rst
End Function
So, create a PT query called (for this example) qryPT
Try omitting the “DAO.” Prefix in your Recordset and dimension statements. Later versions of Access understand what you want.

How do I use a recordset in vba and add it to a SQL select query?

I am trying to get VBA to run a select query in a netezza database and then insert the results from that into a table in an Oracle database table.
Here is my code so far:
Sub Netezza_to_Oracle_table()
Dim mcon As ADODB.Connection
Dim mConnectionString As String
Dim mrecset As New ADODB.Recordset
Dim mSqlQry As String
Dim con As ADODB.Connection
Dim ConnectionString As String
Dim recset As New ADODB.Recordset
Dim SqlQry As String
Set mcon = New ADODB.Connection
Set mrecset = New ADODB.Recordset
Set con = New ADODB.Connection
Set recset = New ADODB.Recordset
mConnectionString = "dsn=NZSQL;servername=servername;port=1234;database=database;User ID=me01;password=password123"
ConnectionString = "GOODSQL.1;User ID=cheese_data;password=password456;Data Source=ORACLE"
mcon.Open mConnectionString
mSqlQry = "SELECT COLUMNS FROM TABLE WHERE ETC "
mrecset.Open mSqlQry, mcon
con.Open ConnectionString
SqlQry = " INSERT INTO MY_ORACLE_TABLE (SELECT * FROM " & ("mrecset") & " ) "
recset.Open SqlQry, con
recset.Close
mcon.Close
Set mrecset = Nothing
con.Close
Set recset = Nothing
End Sub
The problem I think I have is getting the recordset from
mrecset.Open mSqlQry, mcon
into
SqlQry = " INSERT INTO MY_ORACLE_TABLE (SELECT * FROM " & ("mrecset") & " ) "
As there is a message saying the table does not exist when it gets to
recset.Open SqlQry, con,
If I replace the SQL in SqlQry with
select * from MY_ORACLE_TABLE
then it runs that code.
Am I doing this all wrong or is there something relatively simple I can add/change to get it to work? (Or can I somehow create a virtual temp table and refer to that?)
Thanks
It appears it can't be done like this with VBA. I'm going to export the netezza dataset (copyfromrecordset) to excel and then use a sql loader to import the data to the oracle table. I know how to do this already but was hoping for a slicker and quicker (one click) solution.
I did manage to export the data to excel and use a loop to import the data into the oracle table but it was taking far too long to got through the 70k+ rows so will revert to the sql loader.

VBA - ADODB.Connection - Using parameters and retrieving records affected count

Using MS Access to complete this project.
I am attempting to simplify my ADODB code by removing the need for the ADODB.Command object. There are two requirements, the need to use parameters and the need to retrieve records affected (for verification that the SQL executed properly).
The syntax I am attempting to use was mentioned in an article documented in the code block.
{connection object}.[{name of query}] {parameter 1, ..., parameter n [, record set object]}
cn.[TEST_ADODB_Connection] 204, Date & " " & Time(), rs
Sub TEST_ADODB_Connection()
'https://technet.microsoft.com/en-us/library/aa496035(v=sql.80).aspx
'Using ADODB without the use of .Command
Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim lngRecordsAffected As Long
Set cn = CurrentProject.Connection
'TEST_ADODB_Connection Query
'INSERT INTO tbl_Log ( LogID_Orig, LogMessage )
'SELECT [NewLogID] AS _LogID, [NewLogMessage] AS _LogMessage;
Set rs = New ADODB.Recordset
cn.[TEST_ADODB_Connection] 204, Date & " " & Time(), rs
lngRecordsAffected = rs.RecordCount 'Error 3704 - no records returned
'so this is expected, but how do we
'get records affected by the update query?
Debug.Print lngRecordsAffected
End Sub
UPDATE
Including the original code attempting to be simplified.
The .Command object does provide the functionality I desire, but I am looking for an alternative method if it is feasible.
The article (https://technet.microsoft.com/en-us/library/aa496035(v=sql.80).aspx) provides an example where the .Connection object could be executed using parameters. I am trying to extend that example and obtain records affected.
Sub TEST_ADODB_Command()
Dim cm As ADODB.Command
Dim rs As ADODB.Recordset
Dim iLogID_Auto As Integer
Dim strLogMessage As String
Dim lngRecordsAffected As Long
Set cm = New ADODB.Command
iLogID_Auto = 204
strLogMessage = Date & " " & Time
With cm
Set .ActiveConnection = CurrentProject.Connection
.CommandText = "TEST_ADODB_Connection"
.CommandType = adCmdStoredProc
.NamedParameters = True ' does not work in access
.Parameters.Append .CreateParameter("[NewLogID]", adInteger, adParamInput, , iLogID_Auto)
.Parameters.Append .CreateParameter("[NewLogMessage]", adVarChar, adParamInput, 2147483647, strLogMessage)
Set rs = .Execute(lngRecordsAffected)
Debug.Print lngRecordsAffected
End With
Set rs = Nothing
Set cm = Nothing
End Sub
Thank you for the comments. I believe I have devised what I was searching for.
Two points
ADODB.Command is needed if you want to insert/update and retrieve a record count using parameters using a single .Execute. Examples of this can be found all over the internet including my original post under the update section.
ADODB.Command is NOT needed if you have an insert/update query and a select query. I could not find examples of this method. Below is an example I have come up with.
High level overview of what is going on
Execute the insert/update query. Inserts/Updates will not return a recordSet using the one line method.
Execute a select query. This will return a recordSet, however, I couldn't get the .Count method to work as I would think it should.
tlemaster's suggested link provided a work around in the answer section. The work around is to revise the select query to group the results and use the COUNT(*) to return the count. The returning value is then utilized instead of the .Count method.
Sub TEST_ADODB_Connection()
'https://technet.microsoft.com/en-us/library/aa496035(v=sql.80).aspx
'Using ADODB without the use of .Command and .Parameters
Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim lngRecordsAffected As Long
Dim strDateTime As String
Dim lngID As Long
Set cn = CurrentProject.Connection
strDateTime = Date & " " & Time()
lngID = 204 'random number for example purpose
'TEST_ADODB_Connection INSERT Query
'INSERT INTO tbl_Log ( LogID_Orig, LogMessage )
'SELECT [NewLogID] AS _NewLogID, [NewLogMessage] AS _LogMessage;
'This line will execute the query with the given parameters
'NOTE: Be sure to have the parameters in the correct order
cn.[TEST_ADODB_Connection] lngID, strDateTime
'TEST_ADODB_Select
'SELECT Count(tbl_Log.LogID_Orig) AS recordCount
'FROM tbl_Log
'WHERE tbl_Log.LogID_Orig=[_LogID] AND tbl_Log.LogMessage=[_LogMessage];
'Must initilize recordset object
Set rs = New ADODB.Recordset
'This line will execute the query with given parameters and store
'the returning records into the recordset object (rs)
'NOTE: Again, be sure the parameters are in the correct order
'NOTE: the recordset object is always the last argument
cn.[TEST_ADODB_Select] lngID, strDateTime, rs
'unable to directly utilize the .Count method of recordset
'workaround and more optimal solution is to write the SQL
'to return a count using grouping and Count(*) - see SQL above
lngRecordsAffected = rs("recordCount").Value
'Close recordset object
rs.Close
Debug.Print lngRecordsAffected
End Sub

Store a single value in variable with SQL Select

I have the following vba code. I am using MS Excel 2013. (I have an insert statement in this code as well, but I didn't copy this just to have a better highlight of my problem)
Dim cn As Object
Dim cUserEmail As String
Dim strSqlmail As String
Dim rss As Object
Dim strConnection As String
Set cn = CreateObject("ADODB.Connection")
strConnection = "Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source=C:\ActionList.accdb"
strSqlmail = "SELECT [Email-Adress] FROM MyTable WHERE Name='Test'"
Set rss = cn.Execute(strSqlmail)
cUserEmail = rss.Fields(0).Name
At this point it stores the column name (Email-Adress) in my variable. Running the query in Access returns the column with only my desired value in it.
I think I'm as good as finished, but I cannot seem to find how to get the value. When i change to rss.Field(1) it gives an error saying that the item cannot be found.
If you are using the ADO library then the Execute method returns a RecordSet - see the documentation.
So you need to:
Dim rsResult as Recordset
...
Set rsResult = cn.Execute(strSqlmail)
Do While Not rs.EOF
Debug.Print rs!Email-Adress
rs.MoveNext
Loop
rs.Close
So, when you add your WHERE clause to only return one row, then the loop should only iterate once. Whatever is output to the immediate window is what you should assign to cUserEmail.
The following code gave me the requested value:
Dim cn As Object
Dim cUserEmail As String
Dim strSqlmail As String
Dim rss As Object
Dim strConnection As String
Set cn = CreateObject("ADODB.Connection")
strConnection = "Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source=C:\ActionList.accdb"
strSqlmail = "SELECT [Email-Adress] FROM MyTable WHERE MyTable.[Name]='Test'"
Set rss = cn.Execute(strSqlmail)
cUserEmail = rss.Fields(0)

Why would my recordset be empty after running a Select?

I am trying to get this code to work, but It keeps returning -1 for the RecordCount. The select statement does find values, I went to the database and ran it there. But I am unable to get any values in my program.
Dim quotedPubs As ADODB.Connection
Set quotedPubs = New ADODB.Connection
quotedPubs.Open "PROVIDER=SQLOLEDB; DATA SOURCE=*****;INITIAL CATALOG=*****; User ID=****; Password=****"
Dim hoursPubs As ADODB.Connection
Set hoursPubs = New ADODB.Connection
hoursPubs.Open "PROVIDER=SQLOLEDB; DATA SOURCE=*****;INITIAL CATALOG=*****; User ID=****; Password=****"
Dim lsPubs As ADODB.Recordset
Set lsPubs = New ADODB.Recordset
With lsPubs
.ActiveConnection = quotedPubs
.Open "SELECT ProjectNumber FROM hours h"
' WHERE h.lead = " & sEmpNum & ""
If (lsPubs.RecordCount > 0) Then
arr = lsPubs.GetRows(lsPubs.RecordCount)
.Close
You are openning cursor, but not moving through it, so the result of .RecordCount will allways be -1. Check this.
You correctly pulled the records into the Recordset, but lspubs.RecordCount won't work since the Recordset's cursor is still at the first record. Try replacing
(lsPubs.RecordCount > 0)
with
(not(lsPubs.EOF))