I have the following SQL query:
SELECT SUM(OpenInterest) *(SELECT DISTINCT Future
FROM MTM
WHERE Expiry = [dbo].fx_GetRelativeExpiry(#date, 1, #Code)
and TradeDate = #date
and Code = #Code
and type = #Type
and Class = 'Foreign Exchange Future') / 1000
FROM MTM
WHERE Expiry = [dbo].fx_GetRelativeExpiry(#date, #N, #Code)
and TradeDate = #date
and Code = #Code
and type = #Type
and Class = 'Foreign Exchange Future'
Which I want to use as a function in Excel. The issue is that I reuse parameters many times in the above query and I don't know how to do that in excel without creating a new (and basically redundant) parameter. This is my VBA code:
Function GetTotalOI(TradeDate As Date, Code As String, OptionType As String, N As Integer) As Variant
'Create and open the connection
Dim oConnection As Connection
Set oConnection = New Connection
oConnection.ConnectionString = strConnectionStringYieldX
oConnection.Open
'Create the command object
Dim oCommand As Command
Set oCommand = New Command
oCommand.CommandType = adCmdText
Dim SQLString As String
SQLString = "SELECT SUM(OpenInterest) * (SELECT DISTINCT Future" _
& " FROM MTM" _
& " WHERE Expiry = [dbo].fx_GetRelativeExpiry(?, 1, ?)" _
& " and TradeDate = ?" _
& " and Code = ?" _
& " and type = ?" _
& " and Class = 'Foreign Exchange Future') / 1000" _
& " FROM MTM" _
& " WHERE Expiry = [dbo].fx_GetRelativeExpiry(?, ?, ?)" _
& " and TradeDate = ?" _
& " and Code = ?" _
& " and type = ?" _
& " and Class = 'Foreign Exchange Future'"
oCommand.CommandText = SQLString
oCommand.ActiveConnection = oConnection
oCommand.Parameters.Append oCommand.CreateParameter("Date1a", adDBTimeStamp, adParamInput)
oCommand.Parameters.Append oCommand.CreateParameter("Code1a", adVarChar, adParamInput, 50)
oCommand.Parameters.Append oCommand.CreateParameter("Date2a", adDBTimeStamp, adParamInput)
oCommand.Parameters.Append oCommand.CreateParameter("Code2a", adVarChar, adParamInput, 50)
oCommand.Parameters.Append oCommand.CreateParameter("Typea", adVarChar, adParamInput, 1)
oCommand.Parameters.Append oCommand.CreateParameter("Date1", adDBTimeStamp, adParamInput)
oCommand.Parameters.Append oCommand.CreateParameter("N", adInteger, adParamInput)
oCommand.Parameters.Append oCommand.CreateParameter("Code1", adVarChar, adParamInput, 50)
oCommand.Parameters.Append oCommand.CreateParameter("Date2", adDBTimeStamp, adParamInput)
oCommand.Parameters.Append oCommand.CreateParameter("Code2", adVarChar, adParamInput, 50)
oCommand.Parameters.Append oCommand.CreateParameter("Type", adVarChar, adParamInput, 1)
oCommand.Parameters.Item("Date1a").Value = TradeDate
oCommand.Parameters.Item("Code1a").Value = Code
oCommand.Parameters.Item("Date2a").Value = TradeDate
oCommand.Parameters.Item("Code2a").Value = Code
oCommand.Parameters.Item("Typea").Value = OptionType
oCommand.Parameters.Item("Date1").Value = TradeDate
oCommand.Parameters.Item("Code1").Value = Code
oCommand.Parameters.Item("N").Value = N
oCommand.Parameters.Item("Date2").Value = TradeDate
oCommand.Parameters.Item("Code2").Value = Code
oCommand.Parameters.Item("Type").Value = OptionType
Dim result As New ADODB.Recordset
Set result = oCommand.Execute
Dim resultA As Variant
GetTotalOI = WorksheetFunction.Transpose(result.GetRows)
oConnection.Close
End Function
The code works, but it is a mess. I only need 4 parameters. Any idea how to do it? Like is there a way to specify parameters by name instead of just as ? in the query string?
My connection string looks like this:
Const strConnectionStringYieldX As String = "Provider=SQLNCLI10.1;Data Source=xxxx;Initial Catalog=xxxx;Uid=xxxx;Pwd=xxxx;"
EDIT
To clarify the question, in ADO you have to specify parameters as ? rather than something like #ParamName which means if you use the same parameter twice, you have to recreate the parameter in your code. Which is ugly and unpleasant. So in this query where I really only use 4 parameters, because I repeat them a lot I have to uniquely name and create 11 parameters. So if you read the vba code you'll see I have parameters named date1a, date2a, date1 and date2 - BUT THESE ARE ALL THE SAME DATE! I am certain there is a native way to used some sort of named parameter in the query and thus only have to declare the 4 parameters.
I'm sure there is a proper way to do this but in the end I just made a UDF on the DB which allows me to use only 4 parameters and also certain T-SQL commands and procedures that otherwise wouldn't work. But if someone knows of a suitable alternative, please post it!
Related
My form in MS Access has 10 fields on it - three are date fields updated in sequence - ('Start Date', 'Expected End Date', 'Actual End Date'.) Actual End Date is always NULL the first time the form is updated and Expected End Date may be null on the first few edits too.
When I edit the form and click the update button, a VBA routine calls the SQL Server stored procedure to update the database with changes/new values.
All works good - except if I leave any date fields as NULL (which they have to be for the business process) I get an error when the VBA executes the stored procedure. Please see the code I have used to date (and I have spent many hours on this with no luck)
Error Details: 3421
Application uses a value of the wrong type for the current operation.
Private Sub cmdUpdate_Click()
Dim Con As ADODB.Connection
Dim cmd As ADODB.Command
Dim rtn As Integer
Dim dtStartDate As Date
Dim dtProjectedEnd As Date
Dim dtActualEnd As Date
On Error GoTo HandleErr
Set Con = New ADODB.Connection
Con.ConnectionString = fnProvider
Con.Open
Set cmd = New ADODB.Command
With cmd
.ActiveConnection = Con
.CommandType = adCmdStoredProc
Select Case frmStatus
Case "Add"
.CommandText = "dbo.spAddProjectPhaseDetail"
Case "Edit"
.CommandText = "dbo.spEditProjectPhaseDetail"
End Select
.Parameters.Append .CreateParameter("#PhaseDetailID", adinteger, adparaminput, , Me.txtPhaseDetailID)
.Parameters.Append .CreateParameter("#ProjPhaseID", adinteger, adparaminput, , Me.cboProjPhase)
.Parameters.Append .CreateParameter("#ProjectID", adVarChar, adparaminput, 10, Me.txtProjectID)
.Parameters.Append .CreateParameter("#PhaseLead", adinteger, adparaminput, , Me.cboTestLead)
If IsNull(Me.txtStartDt) Then
MsgBox "You must include a phase start date - this can be an estimate", vbExclamation, "Input Error"
GoTo ExitSub
End If
.Parameters.Append .CreateParameter("#PhaseStartDate", adDate, adparaminput, 9, Me.txtStartDt)
.Parameters.Append .CreateParameter("#PhaseProjectedEndDate", adDBDate, adparaminput, 9, Nz(Me.txtProjectedEnd, Null))
.Parameters.Append .CreateParameter("#PhaseActualEndDate", adDBDate, adparaminput, 9, Nz(Me.txtActualEnd, Null))
.Parameters.Append .CreateParameter("#SoftwareBuild", adVarChar, adparaminput, 20, Nz(Me.txtSoftwareBuild, ""))
.Parameters.Append .CreateParameter("#SysEnvironmentID", adVarChar, adparaminput, 50, Nz(Me.txtSysEnvironment, ""))
.Parameters.Append .CreateParameter("#IsCurrent", adinteger, adparaminput, , Nz(Me.chkCurrent, 0))
.Execute rtn
End With
If rtn = 0 Then
Err.Raise 10005, , "Could not add record to database."
Else
MsgBox IIf(frmStatus = "Edit", "Record Updated", "Record Added"), vbInformation, "Data Update"
End If
ExitSub:
Con.Close
Set Con = Nothing
Set cmd = Nothing
frmStatus = ""
DoCmd.Close acForm, "frmAddProjectPhases"
Exit Sub
HandleErr:
MsgBox "[" & Err.Number & "] " & Err.Description & vbCrLf & "If the error persisits, please contact support."
Call sbSysErrorLogUpdate(Err.Number, Err.Description, "Target:frmAddProjectProjectPhases/cmdUpdate_Click()")
Err.Clear
GoTo ExitSub
The Stored Procedure:
ALTER PROCEDURE [dbo].[spEditProjectPhaseDetail]
#PhaseDetailID Integer,
#ProjPhaseID Integer,
#ProjectID varchar(10),
#PhaseLead integer,
#PhaseStartDate DATE,
#PhaseProjectedEndDate date = NULL,
#PhaseActualEndDate date = NULL,
#SoftwareBuild varchar(50) = NULL,
#SysEnvironmentID varchar(50) = NULL,
#IsCurrent bit = 0
AS
BEGIN
SET NOCOUNT ON;
UPDATE dbo.tblProjPhaseDetail
Set
fPROJ_PHASE_ID = #ProjPhaseID,
fPROJECT_ID = #ProjectID,
fPHASE_LEAD = #PhaseLead,
PHASE_START_DT = #PhaseStartDate,
PHASE_PROJECTED_END_DT = #PhaseProjectedEndDate,
PHASE_ACTUAL_END_DT = #PhaseActualEndDate,
SOFTWARE_BUILD = #SoftwareBuild,
SYS_ENVIRONMENT_ID = #SysEnvironmentID
WHERE PHASE_DETAIL_ID = #PhaseDetailID
END
I'd be grateful for any getting this procedure to run with the Null dates. Thanks
Apologies - the answer is quite simple. I changed the parameter type from adDAte to adDBDate fro the parameters that need to pass null and it worked. Also there was a coding error - I had left the 'size' value in the parameters for the dates - in a previous attempt I had tried passing the dates as strings.-
I am trying to insert values from VB6 to SQL Server with a stored procedure but when I want to execute the process, I get this error:
The precision is invalid
I have already changed so many times the way that I try to enter them that I no longer know what to do.
This is the code in VB6
Dim strSQL As String
strSQL = "INSERT INTO FacturasPendientes (IdFactura,FechaFactura,CodigoProveedor,NombreProveedor," _
& " SubTotal,Iva,Total,FechaVencimiento,DiasDescuento,DescProntoPago,Pagado) VALUES (?,?,?,?,?,?,?,?)"
Dim CmdCont As ADODB.Command
Set CmdCont = New ADODB.Command
With CmdCont
Set .ActiveConnection = Cnn
.CommandType = adCmdStoredProc
.CommandText = "sp_FacturasCompras"
.Parameters.Append CmdCont.CreateParameter("#IdFactura", adVarChar, adParamInput, 50, TxtFactura.Text)
.Parameters.Append CmdCont.CreateParameter("#FechaFactura", adDate, adParamInput, 50, dtpFactura.Value)
.Parameters.Append CmdCont.CreateParameter("#CodigoProveedor", adVarChar, adParamInput, 50, TxtCodigoProveedor.Text)
.Parameters.Append CmdCont.CreateParameter("#NombreProveedor", adVarChar, adParamInput, 100, txtProveedor.Text)
.Parameters.Append CmdCont.CreateParameter("#SubTotal", adNumeric, adParamInput, 18, Round(Val(m_Subtotal), 2))
.Parameters.Append CmdCont.CreateParameter("#Iva", adNumeric, adParamInput, 18, Round(Val(m_Iva), 2))
.Parameters.Append CmdCont.CreateParameter("#Total", adNumeric, adParamInput, 18, Round(Val(m_Total), 2))
.Parameters.Append CmdCont.CreateParameter("#FechaVencimiento", adDate, adParamInput, 50, FechaFinal)
.Parameters.Append CmdCont.CreateParameter("#DiasDescuento", adVarChar, adParamInput, 50, txtDias.Text)
.Parameters.Append CmdCont.CreateParameter("#DescProntoPago", adVarChar, adParamInput, 50, txtDescuento.Text)
.Parameters.Append CmdCont.CreateParameter("#Pagado", adBigInt, adParamInput, chkPago.Value)
.Prepared = True
.Execute
End With
MsgBox "Se Grabaron los datos"
And the stored procedure is this:
ALTER PROCEDURE [dbo].[sp_FacturasCompras]
-- agregamos los valores de la consulta
#IdFactura VARCHAR(50),
#FechaFactura DATE,
#CodigoProveedor VARCHAR(50),
#NombreProveedor VARCHAR(100),
#SubTotal NUMERIC(18),
#Iva NUMERIC(18),
#Total NUMERIC(18),
#FechaVencimiento DATE,
#DiasDescuento VARCHAR(50),
#DescProntoPago VARCHAR(50),
#Pagado BIGINT
AS
BEGIN
INSERT INTO FacturasPendientes (IdFactura, FechaFactura, CodigoProveedor, NombreProveedor,
SubTotal, Iva, Total, FechaVencimiento,
DiasDescuento, DescProntoPago, Pagado)
VALUES (#IdFactura, #FechaFactura, #CodigoProveedor, #NombreProveedor,
#SubTotal, #Iva, #Total, #FechaVencimiento,
#DiasDescuento, #DescProntoPago, #Pagado)
END
The easiest way to call a stored procedure is to use Cnn connection object like this
Cnn.sp_FacturasCompras TxtFactura.Text, dtpFactura.Value, _
TxtCodigoProveedor.Text, txtProveedor.Text, _
Round(Val(m_Subtotal), 2), Round(Val(m_Iva), 2), _
Round(Val(m_Total), 2), FechaFinal, txtDias.Text, _
txtDescuento.Text, chkPago.Value
. . . which looks like an ordinary method call on the Cnn object.
If you have to use ADODB.Command then best would be to use some helper function for initializing ADODB.Parameter instances and ADODB.Command instances like this
Option Explicit
Private m_Subtotal As Double
Private m_Iva As Double
Private m_Total As Double
Private Sub Form_Load()
Dim oCmd As ADODB.Command
Set oCmd = InitCommandHelper("sp_FacturasCompras", _
InitParamHelper(TxtFactura.Text, adVarChar, 50), _
InitParamHelper(dtpFactura.Value, adDBDate), _
InitParamHelper(TxtCodigoProveedor.Text, adVarChar, 50), _
InitParamHelper(txtProveedor.Text, adVarChar, 100), _
InitParamHelper(Round(Val(m_Subtotal), 2), adDecimal, Precision:=18, NumericScale:=2), _
InitParamHelper(Round(Val(m_Iva), 2), adDecimal, Precision:=18, NumericScale:=2), _
InitParamHelper(Round(Val(m_Total), 2), adDecimal, Precision:=18, NumericScale:=2), _
InitParamHelper(FechaFinal, adVarChar, 50), _
InitParamHelper(txtDias.Text, adVarChar, 50), _
InitParamHelper(txtDescuento.Text, adVarChar, 50), _
InitParamHelper(chkPago.Value, adBigInt))
Debug.Print oCmd.Parameters.Count
End Sub
Public Function InitCommandHelper( _
ByVal sStoredProc As String, _
ParamArray Params() As Variant) As ADODB.Command
Const FUNC_NAME As String = "InitCommandHelper"
Dim lIdx As Long
On Error GoTo EH
Set InitCommandHelper = New ADODB.Command
With InitCommandHelper
If InStr(sStoredProc, ".") > 0 Then
.CommandText = sStoredProc
Else
.CommandText = "dbo." & sStoredProc
End If
.CommandType = adCmdStoredProc
.Parameters.Append .CreateParameter("RetVal", adInteger, adParamReturnValue)
lIdx = LBound(Params)
Do While lIdx <= UBound(Params)
.Parameters.Append Params(lIdx)
lIdx = lIdx + 1
Loop
End With
Exit Function
EH:
Debug.Print "Critical error: " & Err.Description & "[Form1." & FUNC_NAME & "]", Timer
End Function
Public Function InitParamHelper( _
ByVal vValue As Variant, _
ByVal eType As ADODB.DataTypeEnum, _
Optional ByVal Size As Long, _
Optional Direction As ADODB.ParameterDirectionEnum = adParamInput, _
Optional ByVal Name As String, _
Optional ByVal Precision As Long, _
Optional ByVal NumericScale As Long) As ADODB.Parameter
Const FUNC_NAME As String = "InitParamHelper"
Const DBL_NUM_LIMIT As Double = 10# ^ 12 ' 922337203685477#
Const EMPTY_GUID As String = "{00000000-0000-0000-0000-000000000000}"
On Error GoTo EH
Select Case eType
Case adVarChar, adVarWChar, adChar, adWChar
'--- trim varchar params'
If Not IsNull(vValue) And Not IsEmpty(vValue) Then
vValue = Left$(vValue, Size)
End If
Case adLongVarChar, adLongVarWChar, adLongVarBinary
'--- fix default size for BLOB params'
If Size = 0 Then
Size = -1
End If
Case adDBTimeStamp
If CDate(vValue) = CLng(CDate(vValue)) Then
eType = adDBDate
ElseIf CLng(CDate(vValue)) = 0 Then
eType = adDBTime
Else
eType = adDate
End If
Case adNumeric, adDecimal, adCurrency
'--- numeric range'
If IsNumeric(vValue) Then
If vValue > DBL_NUM_LIMIT Then
vValue = DBL_NUM_LIMIT
ElseIf vValue < -DBL_NUM_LIMIT Then
vValue = -DBL_NUM_LIMIT
End If
End If
Case adGUID
If Direction = adParamInput Then
If IsEmpty(vValue) Then
vValue = EMPTY_GUID
End If
End If
End Select
Set InitParamHelper = New ADODB.Parameter
InitParamHelper.Name = Name
InitParamHelper.Type = eType
InitParamHelper.Direction = Direction
InitParamHelper.Size = Size
InitParamHelper.Value = vValue
If Precision > 0 Then
InitParamHelper.Precision = Precision
End If
If NumericScale > 0 Then
InitParamHelper.NumericScale = NumericScale
End If
Exit Function
EH:
Debug.Print "Critical error: " & Err.Description & "[Form1." & FUNC_NAME & "]", Timer
Set InitParamHelper = Nothing
End Function
Note that NUMERIC(18) gets parsed as NUMERIC(18, 0) which has 0 digits after floating-point but you use Round(..., 2) for these params so probably an error. Just use NUMERIC(18, 2) or evern better DECIMAL(19, 2) as precision 18 and precision 19 use the same 5 bytes for storage.
Also note that sp_ prefix is reserved for system stored procedures, don't use it for user-defined stored procedures. You have to come up with different prefix like usp_ for sp (without underscore).
I suspect the issue is with the adNumeric columns. Try code like the following:
Dim p As Parameter
With CmdCont
Set p = .CreateParameter("#SubTotal", adNumeric, adParamInput)
p.Precision = 18
p.NumericScale = 2
p.Value = Round(Val(m_Subtotal), 2)
.Parameters.Append p
End With
You can adjust the precision and scale as needed.
I have problem in vb6 when I'm trying to insert parameters that are more than 10, 9 below are fine.
When I try from '#1' to '#9' its fine, the output will be strsql = '1st value'...'9th value', but when I with 10 parameter it outputs strsql = '#1'...'#10'
Set rsOR = New ADODB.Recordset
strSql = SQLParams("DB..sp_Insert '#1','#2','#3','#4','#5','#6','#7','#8','#9','#10'", cbPayor.Text, "COLLECTION", txtORCol.Text, dtCol.Value, UserID, CollectionType, txtAmountCol.Text, "PHP", dtColCash.Value, txtCheque.Text)
clsSession.Execute strSql, rsOR
No error using
'#1'...'#9'
FROM: strSql = SQLParams("DB..sp_Insert '#1',...,'#9'", cbPayor.Text, "COLLECTION")
OUTPUTS: strsql "DB..sp_Insert 'text here',.... ,'COLLECTION'
Error using
'#1'...'#10'
FROM: strSql = SQLParams("DB..sp_Insert '#1',...,'#10'", cbPayor.Text, "COLLECTION")
OUTPUTS: strsql "DB..sp_Insert '#1'...'#10'
After a bit of googling I found this (there should be no 9 field limit):
Dim strSQL As String
Dim cmd As New ADODB.Command
strSQL = "UPDATE MyTable SET " & vbNewLine _
& " NEEDS_ID = #NEEDS_ID, " & vbNewLine _
& " OBJ_ID = #OBJ_ID, " & vbNewLine _
& " OBJ_COMMENTS = #OBJ_COMMENTS, " & vbNewLine _
& " TIME21_ID = #TIME21_ID, " & vbNewLine _
& " WHERE ID = #WHEREID"
With cmd
.ActiveConnection = Cn
.CommandText = strSQL
.Parameters.Append .CreateParameter("#NEEDS_ID", adInteger, adParamInput, 2, 12)
.Parameters.Append .CreateParameter("#OBJ_ID", adInteger, adParamInput, 2, 23)
.Parameters.Append .CreateParameter("#OBJ_COMMENTS", adVarChar, adParamInput, 250, "Some text")
.Parameters.Append .CreateParameter("#TIME21_ID", adInteger, adParamInput, 2, 34)
.Parameters.Append .CreateParameter("#WHEREID", adInteger, adParamInput, 18, 456)
.Execute
End With
I have an online form, data from there needs to be entered into a MSSQL database. I'm trying to create a secure insert statement. With the following code I get this error.
Microsoft OLE DB Provider for SQL Server error '80040e5d'
Parameter name is unrecognized.
<%#language="vbscript" codepage="65001" %>
<% option explicit %>
Dim Form_Name
Dim Form_Email
Form_Name= ProtectSQL(request.Form("Name"))
Form_Email= ProtectSQL(Request.Form("Email"))
Dim objConn
set objConn = Server.CreateObject("ADODB.Connection")
Dim strConn
strConn="provider=SQLOLEDB;Server=localhost;Database=dbname;Uid=username;Pwd=password;"
objConn.open strConn
Dim DateSubmitted
DateSubmitted=now()
Dim strSQL
strSQL = "INSERT INTO tablename(DateSubmitted, Name, Email) VALUES('" & DateSubmitted & "', ?, ?)"
Dim objCmd
set objCmd = Server.Createobject("ADODB.Command")
objCmd.ActiveConnection = objConn
objCmd.CommandText = strSQL
objCmd.CommandType = adCmdText
objCmd.NamedParameters = true
Dim objParam1
Set objParam1 = objCmd.CreateParameter("Name", adVarChar, adParamInput, Len(Form_Name), Form_Name)
objCmd.Parameters.Append objParam1
Dim objparam2
Set objparam2 = objCmd.CreateParameter("Email", adVarChar, adParamInput, Len(Form_Email), Form_Email)
objCmd.Parameters.Append objparam2
objCmd.Execute, , adCmdText And adExecuteNoRecords
objConn.close
Set objConn = Nothing
I have also tried
strSQL = "INSERT into tablename(DateSubmitted,Name,Email)values('" & DateSubmitted & "',#Name,#Email)"
objCmd.Parameters.Append objCmd.CreateParameter("#Name",adVarChar,adParamInput,100,Form_Name)
objCmd.Parameters.Append objCmd.CreateParameter("#Email",adVarChar,adParamInput,100,Form_Email)
With this I get Must declare the scalar variable "#Name".
Both error messages reference the objCmd.Execute line
Is there a better way to do an insert statement? I don't need a recordset with this.
You have 5 placeholders in
strSQL = "INSERT INTO tablename(DateSubmitted, Name, Email) VALUES('" & DateSubmitted & "', ?, ?, ?, ?, ?)"
but add only 2 parameters.
I use code like the following code to perform parameterized queries in classic asp:
public sub sql_execute(sql, parameterArray)
dim cnx
Set cnx=CreateObject("ADODB.Connection")
cnx.Open wfDataConnection
if isArray(parameterArray) then
dim cmd, i
Set cmd = CreateObject("ADODB.Command")
With cmd
.CommandText = sql
.CommandType = adCmdText
.ActiveConnection = cnx
for each i in parameterArray
.Parameters.Append .CreateParameter(i(0), i(1), i(2), i(3), i(4))
next
end with
cmd.execute rowsAffected, , adExecuteNoRecords
else
cnx.execute sql, rowsAffected, adExecuteNoRecords
end if
end sub
Calling it like so:
dim sql, parameterArray
sql = "INSERT INTO table (val1, val2) VALUES (?, ?)"
paramaterArray = Array(_
Array("#p1", adInteger, adParamInput, , val1)_
, Array("#p2", adVarChar, adParamInput, 255, val2)_
)
sql_execute sql, parameterArray
I'm not too sure about the variable names (#p1, #p2, etc) when creating parameters. It doesn't seem to matter what you call the variables, but they do require some kind of name in order for it to work.
I've set up a Stored Procedure in SQL Server that works fine. I can now call it from VBA, but want to return a value to know if there were any errors etc. The last parameter in my SP is set up as OUTPUT:
#DataSetID int = 0,
#Destination char(1)='-',
#errStatusOK bit OUTPUT
My VBA to call the SP is below, but it won't work now, after adding the new parameter and I'm not sure where I'm going wrong, I keep getting 3708 - Parameter object is improperly defined. Inconsistent or incomplete information was provided.:
Set cnn = New adodb.Connection
cnn.ConnectionString =
"DRIVER=SQL Server;SERVER=SERVER\SERVER;DATABASE=a_db;Trusted_Connection=Yes"
cnn.Open cnn.ConnectionString
Set cmd = New adodb.Command
cmd.ActiveConnection = cnn
cmd.CommandType = adCmdStoredProc
cmd.CommandText = "stprMoveDataSet"
Set param = cmd.CreateParameter
("#DataSetID", adInteger, adParamInput, , stDataSet)
cmd.Parameters.Append param
Set param = cmd.CreateParameter
("#Destination", adChar, adParamInput, 1, stDestination)
cmd.Parameters.Append param
Set param = cmd.CreateParameter
("#errStatusOK", adBit, adParamReturnValue)
cmd.Parameters.Append param
rs.CursorType = adOpenStatic
rs.CursorLocation = adUseClient
rs.LockType = adLockOptimistic
rs.Open cmd
How can I get the vba to work with the OUTPUT parameter and make the return value 'readable' by the vba.
EDIT - I've changed the question to be more specifically about returning values and not just about using OUTPUT Parameters.
Several ways are possible to get values back using VBA.
Recordset
Count of records affected (only for Insert/Update/Delete otherwise -1)
Output parameter
Return value
My code demonstrates all four. Here is a stored procedure that returns a value:
Create PROCEDURE CheckExpedite
#InputX varchar(10),
#InputY int,
#HasExpedite int out
AS
BEGIN
Select #HasExpedite = 9 from <Table>
where Column2 = #InputX and Column3 = #InputY
If #HasExpedite = 9
Return 2
Else
Return 3
End
Here is the sub I use in Excel VBA. You'll need reference to Microsoft ActiveX Data Objects 2.8 Library.
Sub CheckValue()
Dim InputX As String: InputX = "6000"
Dim InputY As Integer: InputY = 2014
'open connnection
Dim ACon As New Connection
'ACon.Open ("Provider=SQLOLEDB;Data Source=<SqlServer>;" & _
' "Initial Catalog=<Table>;Integrated Security=SSPI")
'set command
Dim ACmd As New Command
Set ACmd.ActiveConnection = ACon
ACmd.CommandText = "CheckExpedite"
ACmd.CommandType = adCmdStoredProc
'Return value must be first parameter else you'll get error from too many parameters
'Procedure or function "Name" has too many arguments specified.
ACmd.Parameters.Append ACmd.CreateParameter("ReturnValue", adInteger, adParamReturnValue)
ACmd.Parameters.Append ACmd.CreateParameter("InputX", adVarChar, adParamInput, 10, InputX)
ACmd.Parameters.Append ACmd.CreateParameter("InputY", adInteger, adParamInput, 6, InputY)
ACmd.Parameters.Append ACmd.CreateParameter("HasExpedite", adInteger, adParamOutput)
Dim RS As Recordset
Dim RecordsAffected As Long
'execute query that returns value
Call ACmd.Execute(RecordsAffected:=RecordsAffected, Options:=adExecuteNoRecords)
'execute query that returns recordset
'Set RS = ACmd.Execute(RecordsAffected:=RecordsAffected)
'get records affected, return value and output parameter
Debug.Print "Records affected: " & RecordsAffected
Debug.Print "Return value: " & ACmd.Parameters("ReturnValue")
Debug.Print "Output param: " & ACmd.Parameters("HasExpedite")
'use record set here
'...
'close
If Not RS Is Nothing Then RS.Close
ACon.Close
End Sub
Set cnn = New adodb.Connection
cnn.ConnectionString =
"DRIVER=SQL Server;SERVER=SERVER\SERVER;DATABASE=a_db;Trusted_Connection=Yes"
cnn.Open cnn.ConnectionString
Set cmd = New adodb.Command
cmd.ActiveConnection = cnn
cmd.CommandType = adCmdStoredProc
cmd.CommandText = "stprMoveDataSet"
Set param1 = cmd.CreateParameter
("#DataSetID", adInteger, adParamInput, , stDataSet)
cmd.Parameters.Append param
Set param2 = cmd.CreateParameter
("#Destination", adChar, adParamInput, 1, stDestination)
cmd.Parameters.Append param
Set param3 = cmd.CreateParameter
("#errStatusOK", adBit, adParamOutput, , adParamReturnValue)
cmd.Parameters.Append param
rs.CursorType = adOpenStatic
rs.CursorLocation = adUseClient
rs.LockType = adLockOptimistic
rs.Open cmd
I'd initially looked at OUTPUT Parameters, but could not find out how to get them back to Access (in VBA) to then provide feedback to the user. A colleague suggested using a SELECT in the Stored procedure and to use this.
STORED PROCEDURE:
Added the following at the end:
SELECT #errStatusOK as errStatusOK, #countCurrent as countCurrent, #countHistorical as countHistorical
VBA:
Dim cnn As ADODB.Connection
Dim cmd As New ADODB.Command, rs As New ADODB.Recordset, param As New ADODB.Parameter
Dim fld As ADODB.Field
Dim stMessage As String
Set cnn = New ADODB.Connection
cnn.ConnectionString = "DRIVER=SQL Server;SERVER=SERVER\SERVER;DATABASE=a_db;Trusted_Connection=Yes"
cnn.Open cnn.ConnectionString
Set cmd = New ADODB.Command
cmd.ActiveConnection = cnn
cmd.CommandType = adCmdStoredProc
cmd.CommandText = "stprMoveDataSet"
Set param = cmd.CreateParameter("#DataSetID", adInteger, adParamInput, , stDataSet)
cmd.Parameters.Append param
Set param = cmd.CreateParameter("#Destination", adChar, adParamInput, 1, stDestination)
cmd.Parameters.Append param
rs.CursorType = adOpenStatic
rs.CursorLocation = adUseClient
rs.LockType = adLockOptimistic
'rs.Open cmd
Set rs = cmd.Execute
If rs!errstatusok = True Then
stMessage = "Operation appears to have been successful, check the DataSets Listing..." & Chr(13) & "Also, the Server returned the following information: ["
Else
stMessage = "Operation appears to have failed, check the DataSets Listing..." & Chr(13) & "Also, the Server returned the following information: ["
End If
For Each fld In rs.Fields
stMessage = stMessage & "| " & fld.Name & " / " & fld.Value & " |"
Next fld
stMessage = stMessage & "]"
MsgBox stMessage
This returns the folliwing:
Operation appears to have failed, check the DataSets Listing...
Also, the Server returned the following information: [| errStatusOK / False || countCurrent / 0 || countHistorical / 10 |]
Among the other parameter enumerations from which "adParamInput" is taken, another is "adParamOutput", which is to indicate an out parameter from a stored procedure, and "adParamInputOutput" for a parameter which goes "both directions," as it were. In your case, I believe "adParamOutput" would be appropriate. I hope this is what you're looking for.