Error trying to call stored procedure with prepared statement - sql

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

Related

Cannot set Parameter size when using adLongVarChar in MS Access

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

VBA Recordset doesn't return all fields

I just startied working with this database and I have a small problem.
So the main idea behind this is to use VBA to get needed information from database that I can use later on.
I am using ADO recordset and connect sting to connect to server. All is fine apart from one problem: when I am creating RecordSet by using SQL request it only returns one field when i know there should me more. At the moment I think that RecordSet is just grabbing first result and storing it in but looses anything else that should be there. Can you please help me.
Here is my code:
'Declare variables'
Dim objMyConn As ADODB.Connection
Dim objMyCmd As ADODB.Command
Dim objMyRecordset As ADODB.Recordset
Dim fldEach As ADODB.Field
Dim OrderNumber As Long
OrderNumber = 172783
Set objMyConn = New ADODB.Connection
Set objMyCmd = New ADODB.Command
Set objMyRecordset = New ADODB.Recordset
'Open Connection'
objMyConn.ConnectionString = "Provider=SQLOLEDB;Data Source=Local;" & _
"Initial Catalog=SQL_LIVE;"
objMyConn.Open
'Set and Excecute SQL Command'
Set objMyCmd.ActiveConnection = objMyConn
objMyCmd.CommandText = "SELECT fldImage FROM tblCustomisations WHERE fldOrderID=" & OrderNumber
objMyCmd.CommandType = adCmdText
'Open Recordset'
Set objMyRecordset.Source = objMyCmd
objMyRecordset.Open
objMyRecordset.MoveFirst
For Each fldEach In objMyRecordset.Fields
Debug.Print fldEach.Value
Next
At the moment Debug returns only one result when it should return two because there are two rows with the same OrderID.
The recordset only opens a single record at a time. You are iterating through all the fields in a single record. Not each record in the recordset.
If your query returns two records, you need to tell the Recordset to advance to the next one.
A query returns one recordset which has some number of records which have some number of fields.
You are iterating through the fields only for one record in the returned recordset.
You can do this with a few ways, but I generally do something like:
objMyRecordset.MoveFirst
Do
If Not objMyRecordset.EOF Then
debug.print "Record Opened - only returning 1 field due to SQL query"
For Each fldEach In objMyRecordset.Fields
Debug.Print fldEach.Value
Next
'this moves to the NEXT record in the recordset
objMyRecordset.MoveNext
Else
Exit Do
End If
Loop
Note that if you want to include more fields you will need to modify this line:
objMyCmd.CommandText = "SELECT fldImage FROM tblCustomisations WHERE fldOrderID=" & OrderNumber
To include whatever additional fields you want returned.
In addition to the #enderland's answer, you can also have a disconnected RecordSet, that have all the values and fields ready for consumption. It's handy when you need to pass the data around or need to close the connection fast.
Here's a function that returns a disconnected RecordSet:
Function RunSQLReturnRS(sqlstmt, params())
On Error Resume next
' Create the ADO objects
Dim rs , cmd
Set rs = server.createobject("ADODB.Recordset")
Set cmd = server.createobject("ADODB.Command")
' Init the ADO objects & the stored proc parameters
cmd.ActiveConnection = GetConnectionString()
cmd.CommandText = sqlstmt
cmd.CommandType = adCmdText
cmd.CommandTimeout = 900 ' 15 minutos
collectParams cmd, params
' Execute the query for readonly
rs.CursorLocation = adUseClient
rs.Open cmd, , adOpenForwardOnly, adLockReadOnly
If err.number > 0 then
BuildErrorMessage()
exit function
end if
' Disconnect the recordset
Set cmd.ActiveConnection = Nothing
Set cmd = Nothing
Set rs.ActiveConnection = Nothing
' Return the resultant recordset
Set RunSQLReturnRS = rs
End Function
You are mixing up terms in your question which makes it unclear
In your first paragraph you describe a problem with "Fields", in the last paragraph you turn it into "Rows". Not exactly the same.
But whatever you are trying to achieve, the code you wrote will only return one field and one row.
If you want all FIELDS, your query should be:
objMyCmd.CommandText = "SELECT * FROM tblCustomisations WHERE fldOrderID=" & OrderNumber
If you want all ROWS, your loop should be:
objMyRecordset.MoveFirst
If Not objMyRecordset.BOF Then
While Not objMyRecordset.EOF
debug.print objMyRecordset!fldImage
RS.MoveNext
Wend
End If

Fetching values from sql 2005 using vbscript

I am designing a web page that will fetch records for a specific id and print the information.I am just trying to redirect user to other page if provided id does not exists.I tried the below code but when id is null its showing blank page instead of redirecting to desired page.
code:
<%
Set conn = Server.CreateObject("ADODB.Connection")
conn.ConnectionString= "Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=eseva;Data Source=BHAGWAT-PC"
conn.open
set rs=Server.CreateObject("ADODB.recordset")
rs.Open "Select * from saat_bara where id='"&request.form("t1")&"'" , conn
if IsNull(rs.Fields("id").Value) then
response.redirect("end.asp")
else
while not rs.eof
response.Write("Token no:")
response.Write(rs.fields.item(0))
response.write("<br><br>")
response.Write("Name:")
response.Write(rs.fields.item(1))
response.write("<br><br>")
response.Write("Address:")
response.Write(rs.fields.item(2))
response.write("<br><br>")
response.Write("Bdate:")
response.Write(rs.fields.item(3))
response.write("<br><br>")
rs.movenext
wend
end if
%>
You can't check a field that does not exist against null. So:
rs.Open "Select * from saat_bara where id='"&request.form("t1")&"'" , conn
if rs.eof
response.redirect("end.asp")
else
while not rs.eof
...
end if
Update:
Listed somethings you may want to consider for the future;
Look into using the ADODB.Command object to build parameterised queries.
Protects against SQL Injection
Data type negotiation is done for you (no adding apostrophes in your query when dealing with string types).
No need to manual setup ADODB.Connection and close it as .ActiveConnection can take a connection string and build your ADODB.Connection for you and when your ADODB.Command is released so is the associated connection.
Return only the field you need in your SQL query instead of using SELECT * depending on the table size this can be very costly (in your code your only returning four fields).
If you are just displaying data to screen consider using .GetRows() to return an Array rather than using ADODB.Recordset for iterating through your returned resultset. Resources that would be otherwise used by the ADODB.Recordset can be released as all your data is contained in a 2 dimensional array.
Below is an example of your code using ADODB.Command and Arrays;
<%
Dim connstr, sql, cmd, rs, data
Dim row, rows
connstr = "Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=eseva;Data Source=BHAGWAT-PC"
sql = ""
sql = sql & "SELECT Field0, Field1, Field2, Field3 " & vbCrLf
sql = sql & "FROM saat_bara " & vbCrLf
sql = sql & "WHERE id = ?"
Set cmd = Server.CreateObject("ADODB.Command")
With cmd
.ActiveConnection = connstr
.CommandType = adCmdText
.CommandText = sql
'As you put apostrophes around your id assumed it must be a varchar. If
'this is used as your primary key would be more efficient for it to be
'a numeric type like int, in which case you would use adInteger.
.Parameters.Append(.CreateParameter("#id", adVarChar, adParamInput, 50)
Set rs = .Execute(, Array(Request.Form("t1")))
If Not rs.EOF Then data = rs.GetRows()
'Release memory used by recordset
Call rs.Close()
Set rs = Nothing
End With
'Release memory and close connection used by command.
Set cmd = Nothing
If IsArray(data) Then
rows = UBound(data, 2)
For row = 0 To rows
'Consider not using Response.Write in your loop (taken from Bond's suggestion)
Call Response.Write("Token no:")
Call Response.Write(data(0, row))
Call Response.Write("<br><br>")
Call Response.Write("Name:")
Call Response.Write(data(1, row))
Call Response.Write("<br><br>")
Call Response.Write("Address:")
Call Response.Write(data(2, row))
Call Response.Write("<br><br>")
Call Response.Write("Bdate:")
Call Response.Write(data(3, row))
Call Response.Write("<br><br>")
Next
Else
'No data redirect
Call Response.Redirect("end.asp")
End If
%>

VBA, ADO.Connection and query parameters

I have excel VBA script:
Set cоnn = CreateObject("ADODB.Connection")
conn.Open "report"
Set rs = conn.Execute("select * from table" )
Script work fine, but i want to add parameter to it. For example " where (parentid = myparam)", where myparam setted outside query string. How can i do it?
Of course i can modify query string, but i think it not very wise.
You need to use an ADODB.Command object that you can add parameters to. Here's basically what that looks like
Sub adotest()
Dim Cn As ADODB.Connection
Dim Cm As ADODB.Command
Dim Pm As ADODB.Parameter
Dim Rs as ADODB.Recordset
Set Cn = New ADODB.Connection
Cn.Open "mystring"
Set Cm = New ADODB.Command
With Cm
.ActiveConnection = Cn
.CommandText = "SELECT * FROM table WHERE parentid=?;"
.CommandType = adCmdText
Set Pm = .CreateParameter("parentid", adNumeric, adParamInput)
Pm.Value = 1
.Parameters.Append Pm
Set Rs = .Execute
End With
End Sub
The question mark in the CommandText is the placeholder for the parameter. I believe, but I'm not positive, that the order you Append parameters must match the order of the questions marks (when you have more than one). Don't be fooled that the parameter is named "parentid" because I don't think ADO cares about the name other than for identification.
Alternative example returning a command from a function:
Function BuildCommand(conn As ADODB.Connection) As ADODB.Command
Dim cmd As ADODB.Command
Set cmd = New ADODB.Command
cmd.ActiveConnection = conn
cmd.CommandType = adCmdText
cmd.Parameters.Append cmd.CreateParameter("#name", adVarChar, adParamInput, 255, "Dave")
cmd.CommandText = "SELECT * FROM users WHERE name = #name;"
Set BuildCommand = cmd
End Function
A couple things to note:
When using adVarChar data type, the size argument to cmd.CreateParameter (e.g. 255) is required. Not supplying it results a run-time error 3708: Application-defined or object-defined error, as indicated in the documentation:
If you specify a variable-length data type in the Type argument, you must either pass a Size argument or set the Size property of the Parameter object before appending it to the Parameters collection; otherwise, an error occurs.
If the cmd.ActiveConnection property is set when cmd.CommandText is set, and cmd.CommandText contains named parameters, cmd.Parameters will be populated accordingly. Calling cmd.Parameters.Append afterwards could result in duplicates. For example:
cmd.ActiveConnection = conn
cmd.CommandType = adCmdText
Debug.Print cmd.Parameters.Count ' 0
cmd.CommandText = "SELECT * FROM users WHERE name = #name;"
Debug.Print cmd.Parameters.Count ' 1
cmd.Parameters.Append cmd.CreateParameter("#name", adVarChar, adParamInput, 255, "Dave")
Debug.Print cmd.Parameters.Count ' 2
I believe this is what is meant in the documentation, which is slightly inaccurate:
If the Prepared property of the Command object is set to True and the Command object is bound to an open connection when you set the CommandText property, ADO prepares the query (that is, a compiled form of the query that is stored by the provider) when you call the Execute or Open methods.
As a workaround, either set cmd.CommandText or cmd.ActiveConnection after adding parameters.

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.