Cannot set Parameter size when using adLongVarChar in MS Access - vba

I have a function that makes a call to a SQL Server database to return a String value. Previously, expected string values were fairly short, but as the character length can exceed 255 on occasion, I have changed the SQL field type in the table to nvarchar(MAX).
In the project, I have altered the code to use adLongVarChar instead of adVarChar and need to specify the length. As there is no length, I know for Input parameters, -1 is the correct length to use. However, when using this as an Output parameter, I receive Run-Time Error 3708 - 'Parameter object is improperly defined. Inconsistent or incomplete information was provided.'
What is the correct syntax to construct the paramter?
Code below:
Public Function getStatus() As String
Dim db As ADODB.Connection
Dim rs As ADODB.Recordset
Dim sp As ADODB.Command
Dim rp1 As ADODB.Parameter
Dim rp2 As ADODB.Parameter
'// Data declarations
Set db = New ADODB.Connection
Set rs = New ADODB.Recordset
Set sp = New ADODB.Command
Set rp1 = sp.CreateParameter("#ksActive", adBoolean, adParamOutput)
Set rp2 = sp.CreateParameter("#ksMsg", adLongVarChar, adParamOutput, -1)
'// Data connection initialisation
db.Open dbString
With sp
.ActiveConnection = db
.CommandText = "dbo.TestProcedureName"
.CommandType = adCmdStoredProc
.Parameters.Append rp1 '// This works fine
.Parameters.Append rp2 '// Error occurs here
.Execute
End With
getStatus = rp2.Value
End Function

TL/DR: Use .Parameters.Refresh and .NamedParameters = True to define output (return) parameters in MS Access ADODB.
I just came across this and had this same problem. I found a hint on MSDN "What is the ADO DataType for nVarchAR(max)". For whatever reason, you MUST include an initial value with the length (at least in Access VBA) for long value parameters in Access ADO. I found adding NULL works just fine to satisfy it, especially when you set the values later for stored commands.
NOTE: This will only work for input values (as shown here, outputs cannot be defined like this)
' Note: only works for output params.
Set rp2 = sp.CreateParameter("#ksMsg", adLongVarChar, adParamInput, -1, NULL)
Output Parameter definition:
Using OP's names:
Public Function getStatus() As String
Dim db As ADODB.Connection
Dim rs As ADODB.Recordset
Dim sp As ADODB.Command
Dim rp1 As ADODB.Parameter
Dim rp2 As ADODB.Parameter
'// Data declarations
Set db = New ADODB.Connection
Set rs = New ADODB.Recordset
Set sp = New ADODB.Command
'// Data connection initialisation
db.Open dbString
Dim sp As ADODB.Command
With sp
.ActiveConnection = db
.CommandText = "dbo.TestProcedureName"
.CommandType = adCmdStoredProc
.Parameters.Refresh
.NamedParameters = True
Set rp1 = .Parameters("#ksActive")
Set rp2 = .Parameters("#ksMsg")
' Set outputs to null because they're cached, and if your
' second call returns nothing, you'll get the prior call's results.
rp1.Value = Null
rp2.Value = Null
.Execute
End With
getStatus = rp2.Value
End Function
You need to use sp.Parameters.Refresh to define output parameters for stored procedures. After scouring the internet, it appears that there's an issue with using MS Access ADODB adLongVarWChar (or any long / wide return variable) and adParamOutput or adParamInputOutput and defining them explicitly.
I have found the only way to get Output Parameters to work is to use .Parameters.Refresh; once I did that, it works just fine (and returns correctly).
Just be sure to clear them out before executing (set outputs to null) or you'll get the previously cached result back (and it will send you on a wild goose chase).

Related

Using VBA to call a stored procedure in SQL Server, passing one parameter. I'm getting an error, multiple-step OLD DB

Going round in circles on this. I'm using VBA to call a stored procedure to pull in some data from SQL Server.
It passes one parameter which is valuation date. I have tested this SQL Server stored procedure and it works fine in sql with all dates. Now the strange thing is the query works perfectly fine from VBA and SQL for dates 10/31/2019 and previous, but for 11/30/2019 and 12/31/2019, I get an error
Run-time error '-20147217887 (8004e21)': Multiple-step OLe DB operation generated errors.
I can't figure out why it would work for the some dates but not others when it works for all date directly in SQL. Thanks in advance.
Here is the VBA code:
Sub GroupExperience()
'SQL code
Dim ValDate As Date
ValDate = Format(Range("ValDate"), "mm-dd-yyyy")
Dim rs As ADODB.Recordset
Dim cnSQL As ADODB.Connection
Dim sqlcommand As ADODB.Command, prm As Object
Set cnSQL = New ADODB.Connection
cnSQL.Open "Provider=SQLOLEDB; Data Source=bddc1didw1;Initial Catalog=Actuarial; Trusted_connection=Yes; Integrated Security='SSPI'"
Set sqlcommand = New ADODB.Command
sqlcommand.ActiveConnection = cnSQL
'GroupExperience
sqlcommand.CommandType = adCmdStoredProc
sqlcommand.CommandText = "[HRT\akapur].[AllGroupExperience]"
Set prm = sqlcommand.CreateParameter("ValDate", adDate, adParamInput)
sqlcommand.Parameters.Append prm
sqlcommand.Parameters("ValDate").Value = ValDate
Set rs = New ADODB.Recordset
rs.CursorType = adOpenStatic
rs.LockType = adLockOptimistic
rs.Open sqlcommand
Sheets("Experience Data").Range("A2").CopyFromRecordset rs
Dim pt As PivotTable
Set pt = Sheets("Experience").PivotTables("PivotTable1")
pt.RefreshTable
End Sub

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

Must declare scalar variable when using parameterized sql query

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.

Error trying to call stored procedure with prepared statement

I'm trying to use a prepared statement to call a stored procedure (using ADODB with classic ASP), but when I set CommandType I get the following error:
ADODB.Command error '800a0bb9'
Arguments are of the wrong type, are out of acceptable range, or are in conflict with one another.
I have the following code:
With Server.CreateObject("ADODB.Command")
.ActiveConnection = db 'this is initialized prior
.CommandType = adCmdStoredProc
.CommandText = "procName"
End With
The prepared statement name is correct (I'm able to call it just by executing the string), and if I leave out the .CommandType and try calling .Execute, I get an error specifying:
Procedure or function 'procName' expects parameter '#ParamName', which was not supplied.
Even if I leave out the CommandType, I have no idea how to actually add the parameter (something along the following lines just results in the original error about arguments of the wrong type):
.Parameters.Append .CreateParameter("#ParamName",adVarChar,adParamInput,50,param)
I've also tried the following and got an error "Item cannot be found in the collection corresponding to the requested name or ordinal."
.Parameters.Refresh
.Parameters(0) = param
I've looked at several examples of how to call stored procedures using prepared statements, and it looks like I'm using the right syntax, but anything I try seems to result in some kind of error. Any help would be greatly appreciated.
You want something like this (untested)
Dim cmd, rs, ars, conn
Set cmd = Server.CreateObject("ADODB.Command")
With cmd
'Assuming passing connection string if passing ADODB.Connection object
'make sure you use Set .ActiveConnection = conn also conn.Open should
'have been already called.
.ActiveConnection = conn
'adCmdStoredProc is Constant value for 4 (include adovbs or
'set typelib in global.asa)
.CommandType = adCmdStoredProc
.CommandText = "dbo.procName"
'Define parameters in ordinal order to avoid errors
Call .Parameters.Append(.CreateParameter("#ParamName", adVarChar, adParamInput, 50))
'Set values using parameter friendly name
.Parameters("#ParamName").Value = param
'Are you returning a recordset?
Set rs = .Execute()
'Populate array with data from recordset
If Not rs.EOF Then ars = rs.GetRows()
Call rs.Close()
Set rs = Nothing
End With
Set cmd = Nothing
It is important to remember that the friendly name (as I rule I tend to match my parameter names in my stored procedure to my friendly names in ADO) you give your parameter means nothing to the stored procedure as ADO passes the parameters ordinally and nothing more, the fact you get the error;
Procedure or function 'procName' expects parameter '#ParamName', which was not supplied.
Suggests that the stored procedure is expecting your #ParamName parameter (defined in your stored procedure) value to be passed from ADO in a different ordinal position, which usually means you have not defined all your parameters or passed all the parameter values in the position they are expected.
You can also do a shortened version if your confident of your ordinal positioning and parameter requirements
With cmd
.ActiveConnection = conn
.CommandType = adCmdStoredProc
.CommandText = "dbo.procName"
'Pass parameters as array following ordinal position.
Set rs = .Execute(, Array(param))
'Populate array with data from recordset
If Not rs.EOF Then ars = rs.GetRows()
Call rs.Close()
Set rs = Nothing
End With
Set cmd = Nothing
Working with a 2-dimensional array is easy and negates the overhead of working directly with a ADODB.Recordset.
Dim row, rows
If IsArray(ars) Then
rows = UBound(ars, 2)
For row = 0 To rows
Response.Write "First column from row " & row & " = " & ars(0, row) & "<br />"
Next
Else
Response.Write "No data to return"
End If
Links
Using METADATA to Import DLL Constants - If your having trouble with the ADO constants (adCmdStoredProc etc.) this will fix it for you.
Here is how you call a stored procedure in ASP classic:
'Set the connection
'...............
'Set the command
DIM cmd
SET cmd = Server.CreateObject("ADODB.Command")
SET cmd.ActiveConnection = Connection
'Set the record set
DIM RS
SET RS = Server.CreateObject("ADODB.recordset")
'Prepare the stored procedure
cmd.CommandText = "procName"
cmd.CommandType = 4 'adCmdStoredProc
'Assign value to the parameter
cmd.Parameters("#ParamName ") = ParamValue
'Execute the stored procedure
RS = cmd.Execute
SET cmd = Nothing
'You can now access the record set
if (not RS.EOF) THEN
data = RS("column_name")
end if
'dispose your objects
RS.Close
SET RS = Nothing
Connection.Close
SET Connection = Nothing

Calling Oracle Stored procedure from VBA ADODB

Hi
I am trying to use a stored procedure in WORD VBA to retrieve some addresses using a stored procedure to populate a list field.
Private Sub txtCpny_AfterUpdate()
Dim rst As ADODB.Recordset
Dim cmd As ADODB.Command
Dim param1 As ADODB.Parameter
Dim param2 As ADODB.Parameter
Dim strCpny As String
strCpny = GetSearchString(Me.txtCpny) 'ie %Name%
Set cmd = CreateObject("ADODB.Command")
With cmd
.ActiveConnection = mcn
.CommandText = "LISTPARTNER_NAME"
.CommandType = adCmdStoredProc
Set param1 = .CreateParameter("RCT1", adInteger, adParamInputOutput, , Null)
Set param2 = .CreateParameter("firmaName", adVarChar, adParamInput, 50, strCpny)
.Parameters.Append param1
.Parameters.Append param2
Set rst = .Execute
End With
...Using the recordset here
rst.Close
Set param1 = Nothing
Set param2 = Nothing
Set cmd = Nothing
End Sub
The Stored Procedure looks as follows: The SQL should result in a recordset holding the matching Companies.
PROCEDURE LISTPARTNER_NAME (
firmaName IN VARCHAR2 DEFAULT NULL,
RCT1 IN OUT GLOBALPKG.RCT1
)
AS
BEGIN
OPEN RCT1 FOR
SELECT
...
FROM
...
WHERE
KNAG.NAME_ORG LIKE LISTPARTNER_NAME.firmaName
...
END LISTPARTNER_NAME
When the command is executed VB throws a RunTime Error
ORA-06550: Line 1, column 13:
PLS-00306: wrong number or types of arguments in call to
'LISTPARTNER_NAME'
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored
In my opinion the I am doing something wrong wit the parameters. Ihave been trying various versions of setting the parameters with no luck
Any Clues?
Thanks
have a look at this thread, it may be of assistance
http://forums.oracle.com/forums/thread.jspa?threadID=360922
The only other thing I could suggest would be switching the order of the parameters (so they are in the same order -- I know ODP defaults to order but I am unsure of ADODB)
I have been successfully using this code in Access:
Function runAdo(sql As String, usr As String, pwd As String)
'by Patrick Honorez - www.idevlop.com ----- 09-nov-2012
'Purpose : run Oracle proc using ADO connection
Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
Set cn = New ADODB.Connection
cn.Open GetPersonalizedConnectStringADO(usr, pwd)
Set rs = New ADODB.Recordset
rs.Open sql, cn, adOpenStatic, adLockReadOnly
cn.Close
End Function
I rarely use ADO from Access, since I find DAO simpler to use, but in this case I had to execute some Oracle procs requiring a different UID, and creating a new DAO Querydef specifying different UID and PWD, did not work, perhaps due to the fact that Access keeps a cache of connections.
So I decided to use ADO for the second "user" and it works like a charm.