Executing parameterised T-SQL stored procedure in Access vba - sql

I'm getting a "Run time error (13): type mismatch" error when I attempt to execute the below sproc from an after update event in Access. My sproc has the #Season as nvarchar(max) and #Year as int in the SQL (MSSQL2014). Any ideas as to the cause? I've been search all day but no joy as yet. Here is the code:
Private Sub Event_Click()
Dim cnn1 As New ADODB.Connection
Dim cmd As New ADODB.Command
Set cnn1 = New ADODB.Connection
cnn1.ConnectionString = "<connection string snipped but works OK>"
cnn1.ConnectionTimeout = 30
cnn1.Open
vSeason = Me.ComboBox1.Value
vYear = Me.ComboBox2.Value
With cmd
.ActiveConnection = cnn1
.CommandText = "dbo.StoredProcedure"
.CommandType = 4
.CommandTimeout = 0
.Parameters.Append .CreateParameter("#Season", adVarChar, adParamInput, vSeason)
.Parameters.Append .CreateParameter("#Year", adInteger, adParamInput, vYear)
End With
cnn1.Close
Set cnn1 = Nothing
Would be grateful for any pointers here. Any further info needed let me know.

I think the problem is that CreateParameter need size for adVarChar datatype before its value.
try something like:
.Parameters.Append .CreateParameter("#Season", adVarChar, adParamInput, 200, vSeason)
in your code the value vSeason is passed as size parameter,
you are passing a string as integer to CreateParameter, this is the type mismatch error
I hope this helps

Related

SQL SP not returning a value to ADO execute

OK, I'm missing something obvious here - I have an SP that takes in an integer ID and returns a string. I've used this SP for quite a while with DAO. Now I need to switch to ADO so I can run it under and existing connection (another question I'll post elsewhere).
So my code follows. It returns no errors but it also returns no results. The output parameter is null. What am I missing?
Dim adoCon As ADODB.Connection
Dim adoCMD As ADODB.Command
Dim SQLstr As String
Dim ConStr As String
'--- get connection string from existing object, but strip leading odbc; piece
ConStr = Replace(CurrentDb.TableDefs("[TableName]").Connect, "ODBC;", "")
Set adoCon = New ADODB.Connection
adoCon.ConnectionString = ConStr
adoCon.Open
Set adoCMD = New ADODB.Command
With adoCMD
.ActiveConnection = adoCon
.CommandType = adCmdStoredProc
.Parameters.Append .CreateParameter(, adInteger, adParamReturnValue, , Null) ' return value
.Parameters.Append .CreateParameter("Path", adVarChar, adParamOutput, 500, Null)
.Parameters.Append .CreateParameter("AsyID", adInteger, adParamInput)
.Parameters.Item("AsyID").Value = AsyID
.CommandText = "dbo.spGetAncestry"
.Execute
End With
GetHeritage = adoCMD.Parameters(1).Value 'parm(0) = 0; parm(1) = NULL; parm(2) = AsyID
adoCon.Close
Although your code should work. Please remove the optional expressions in the parameter definition and try the following:
Dim rv as Integer
Set adoCMD = New ADODB.Command
With adoCMD
.ActiveConnection = adoCon
.CommandType = adCmdStoredProc
.Parameters.Append .CreateParameter("RETURN_VALUE", adInteger, adParamReturnValue)
.Parameters.Append .CreateParameter("Path", adVarChar, adParamOutput, 500)
.Parameters.Append .CreateParameter("AsyID", adInteger, adParamInput, , AsyID)
.CommandText = "dbo.spGetAncestry"
.Execute
End With
rv = adoCMD.Parameters("RETURN_VALUE").Value
GetHeritage = adoCMD.Parameters("Path").Value
also make sure your SP is returning the correct data type and size for your output parameter and adjust the code accordingly. If you're returning VARCHAR(MAX), then that is treated as a "BLOB" in ADO, see this related question What are the limits for ADO data types?.
In this case you can try returning varchar(8000) from the SP and updating the code accordingly.
Found it.
Apparently, in the ADO call, it doesn't matter what you set the return value to (I was trying to use "" or even " " before I set it to null) when it executes in SQL batch, it is simply set to NULL as it shows in this trace form. For this run, the Output was initialized as " ", but the batch passed in NULL.
[!SQL trace of the above query being executed with " " in the initialization of the Output variable1]1
Normally, a null wouldn't be a problem as the typical SP assignment would be:
SET #Path = [SELECT value from table]
or, if it was a concatenation, you would initialize the variable:
SET #Path = ''
before stringing together the input.
In this particular case, though, the SP is recursive. It calls itself passing an input and the output values to the new copy. Because of this, you can't initialize the value and you can't use a straight assignment. To get around this, I needed to use:
#path = COALESCE(#path, '') + [SELECT value from table]
to trap any NULL passed in.

When executing the stored procedure from VBA getting error "Procedure or function expects parameter #empid which is not passed"

I have written a stored procedure to insert the data into a table. When I execute that into SQL Server 2012 it works fine while in VBA throws error
procedure or function expects parameter #empid which was not provided
I tried to resolve with different combinations of connection strings, CommandType etc but it does not resolve. While code seems perfectly fine, please assist me to resolve it.
Stored procedure:
CREATE PROCEDURE [dbo].[spInsertDataIntoEmployee]
#empid INT,
#empname VARCHAR(20),
#empage INT,
#empsalary DECIMAL(8,2)
AS
BEGIN
SET NOCOUNT ON
INSERT INTO [dbo].[employee] (empid, empname, empage, empsalary)
VALUES (#empid, #empname, #empage, #empsalary)
END
VBA code:
Private Sub Exec_StoredProcFromExcel_Click()
Dim con As ADODB.Connection
Dim res As ADODB.Recordset
Dim mobjCmd As ADODB.Command
Dim strConn As String
Dim par1 As Object
Dim par2 As Object
Dim par3 As Object
Dim par4 As Object
'Dim empname As Varchar
'Dim empage As Integer
'Dim empsalary As Single
Dim indRecordSetFields As Integer
'Dim strQuery As String
'con.Close
Set con = New ADODB.Connection
Set res = New ADODB.Recordset
Set mobjCmd = New ADODB.Command
strConn = "Driver={SQL Server Native Client 11.0};Server=localhost;Database=test;Trusted_Connection=yes;"
'strConn = "Provider=SQLOLEDB; Data Source=localhost; Initial Catalog=test; Integrated Security=SSPI;"
con.Open strConn, adOpenStatic, adLockOptimistic
Set par1 = mobjCmd.CreateParameter("#empid", adInteger, adParamInput)
Set par2 = mobjCmd.CreateParameter("#empname", adVarChar, adParamInput, 30)
Set par3 = mobjCmd.CreateParameter("#empage", adInteger, adParamInput)
Set par4 = mobjCmd.CreateParameter("#empsalary", adDouble, adParamInput)
With mobjCmd
.ActiveConnection = con
.CommandText = "Exec [dbo].[spInsertDataIntoEmployee]"
.CommandType = 1 'adCmdStoredProc=4 does not work while adCmdText=1 and adCmdUnknown=8 both work for me
.CommandTimeout = 45
.Parameters.Append par1
.Parameters("#empid").Value = 111
.Parameters.Append par2
.Parameters("#empname").Value = "majid rajih"
.Parameters.Append par3
.Parameters("#empage").Value = 34
.Parameters.Append par4
.Parameters("#empsalary").Value = 200000
'.Parameters.Append .CreateParameter("#empid", adInteger, adParamInput, 111)
'.Parameters.Append .CreateParameter("#empname", adVarChar, adParamInput, 20, "majid khan")
'.Parameters.Append .CreateParameter("#empage", adInteger, adParamInput, 30)
'.Parameters.Append .CreateParameter("#empsalary", adSingle, adParamInput, 250000)
'.Parameters.Append .CreateParameter("#empid", adInteger, adParamInput, , ThisWorkbook.Sheets("employee").Range("A2").Value)
'.Parameters.Append .CreateParameter("#empname", adVarChar, adParamInput, 20, ThisWorkbook.Sheets("employee").Range("B2").Value)
'.Parameters.Append .CreateParameter("#empage", adInteger, adParamInput, , ThisWorkbook.Sheets("employee").Range("C2").Value)
'.Parameters.Append .CreateParameter("#empsalary", adSingle, adParamInput, , ThisWorkbook.Sheets("employee").Range("D2").Value)
' repeat as many times as you have parameters
.Execute
End With
'With res
'res.CursorType = adOpenStatic
'res.LockType = adLockOptimistic
'res.Open mobjCmd 'This executed the stored proc
'End With
End Sub
You are running the command in the text mode.
Your full query is Exec [dbo].[spInsertDataIntoEmployee] which does not include any parameters.
CommandType should be adCmdStoredProc, and CommandText should be the procedure name, without exec.
.CommandType = adCmdStoredProc
.CommandText = "[dbo].[spInsertDataIntoEmployee]"
If you want to keep adCmdText, list all parameters in the query:
.CommandType = adCmdText
.CommandText = "exec [dbo].[spInsertDataIntoEmployee] #empid, #empname, #empage, #empsalary"
.CommandText = "spInsertDataIntoEmployee"
And try to send everything as nvarchar.

What is the correct type for inserting OLE object?

I'm trying to insert data from a MS Access form in to a table so I've knocked up a short function to see whether it is possible.
The function I have is this:
Function Module2() As Boolean
On Error GoTo Err_Insert
Dim adoCMD As Object
Dim adoRS As Object
Dim strSQL As String
'Define a query to INSERT a new record into the FE temp table
strSQL = "INSERT INTO [Table1] ([A1],[A2],[A3],[img] VALUES (p1,p2,p3,img);"
'Define attachment to database table specifics
Set adoCMD = CreateObject("ADODB.Command")
With adoCMD
.ActiveConnection = CurrentProject.Connection
.CommandType = adCmdText
.Parameters.Append .CreateParameter("p1", adVarChar, adParamInput, Len(Forms("Form1").Controls("text0").value), Forms("Form1").Controls("Text0").value)
.Parameters.Append .CreateParameter("p2", adBoolean, adParamInput, Len(Forms("Form1").Controls("Check2").value), Forms("Form1").Controls("Check2").value)
'.Parameters.Append .CreateParameter("p3", adVarChar, adParamInput, Len(Forms("Form1").Controls("Combo4").value), Forms("Form1").Controls("Combo4").value)
.Parameters.Append .CreateParameter("img", adLongVarBinary, adParamInput, Len(Forms("Form1").OLEBound6), Forms("Form1").Controls("OLEBound6"))
.CommandText = strSQL
Set adoRS = .Execute
End With
'Return a good return code
Module2 = True
Exit_Insert:
'Clean up the connection to the database
Set adoRS = Nothing
Set adoCMD = Nothing
Exit Function
Err_Insert:
Call MsgBox("Class: Module2, Function: Insert()")
Module2 = False
Resume Exit_Insert
End Function
when run, I get the following error:
Run-time error 3421
Application uses a value of the wrong type for the current operation
and breaks at this point:
.Parameters.Append .CreateParameter("img", adLongVarBinary, adParamInput, Len(Forms("Form1").OLEBound6), Forms("Form1").Controls("OLEBound6"))
What is the correct type for an OLEObject? I've tried several all with the same result. I cannot find which DataTypeEnum looks to be the correct type.
Thanks
When you skip parameter P3, the query will try to insert img (now the third parameter) to A3, thus the error.

SQL Parameterized Queries with Different Variable Types from VBA?

In using SQL and parameterized queries from VBA, I am running into the following problem.
When I construct parameters, I can construct varchar and int parameters individually and use them correctly. However when I mix them, I get the following SQL error:
Operand type clash: text is incompatible with int
It seems SQL is smashing everything to text when I combine parameters of multiple types.
What do I have to do differently with my code (either VBA/SQL) to allow the third case to work (with parameters of varying types)?
Here is the VBA code:
Sub testAdodbParameters()
Dim Cn As ADODB.Connection
Dim Cm As ADODB.Command
Dim Pm As ADODB.Parameter
Dim Pm2 As ADODB.Parameter
Dim Rs As ADODB.Recordset
Set Cn = New ADODB.Connection
Cn.Open "validConnectionString;"
Set Cm = New ADODB.Command
On Error GoTo errHandler
With Cm
.ActiveConnection = Cn
.CommandType = adCmdText
Set Pm = .CreateParameter("TestInt", adInteger, adParamInput)
Pm.value = 1
Set Pm2 = .CreateParameter("TestVarChar", adVarChar, adParamInput, -1)
Pm2.value = "testhi"
'this works
If True Then
.CommandText = "INSERT INTO Test(TestInt) VALUES(?);"
.Parameters.Append Pm
End If
'this also works
If False Then
.Parameters.Append Pm2
.CommandText = "INSERT INTO Test(TestVarChar) VALUES(?);"
End If
'this fails with:
'Operand type clash: text is incompatible with int
If False Then
.Parameters.Append Pm
.Parameters.Append Pm2
.CommandText = "INSERT INTO Test(TestVarChar,TestInt) VALUES(?,?);"
End If
Set Rs = .Execute
End With
errHandler:
Debug.Print Err.Description
End Sub
Here is the SQL code to generate the table:
CREATE TABLE Test (
ID int IDENTITY(1,1) PRIMARY KEY,
TestVarChar varchar(50),
TestInt int
);
You are in VARCHAR, INT order in the query, you can try this:
.Parameters.Append Pm2
.Parameters.Append Pm
.CommandText = "INSERT INTO Test(TestVarChar,TestInt) VALUES(?,?);"
Does it work ?

Problem with passing parameters to SQL procedure using VBA

I am trying to pass #intDocumentNo and #intCustomerNo to a stored procedure using VBA but only #intCustomerNo is updated in dbo.tblOrderHead. I don't think the problem is with the procedure because if I enter the values manually it runs properly.
What am I doing wrong?
VBA Code:
Private Sub intCustomerNo_Click()
Dim cmdCommand As New ADODB.Command
With cmdCommand
.ActiveConnection = CurrentProject.Connection
.CommandType = adCmdStoredProc
.CommandText = "uspSelectCustomer"
'#intDocumentNo
.Parameters("#intDocumentNo").Value = Forms![frmSalesOrder].[IntOrderNo]
'#intCustomerNo
.Parameters("#intCustomerNo").Value = Me![intCustomerNo]
.Execute
End With
DoCmd.Close
Forms![frmSalesOrder].Requery
End Sub
Procedure:
UPDATE dbo.tblOrderHead
SET dbo.tblOrderHead.intCustomerNo = #intCustomerNo ,
dbo.tblOrderHead.intPaymentCode = dbo.tblCustomer.intPaymentCode,
dbo.tblOrderHead.txtDeliveryCode = dbo.tblCustomer.txtDeliveryCode,
dbo.tblOrderHead.txtRegionCode = dbo.tblCustomer.txtRegionCode,
dbo.tblOrderHead.txtCurrencyCode = dbo.tblCustomer.txtCurrencyCode,
dbo.tblOrderHead.txtLanguageCode = dbo.tblCustomer.txtLanguageCode
FROM dbo.tblOrderHead
INNER JOIN dbo.tblCustomer ON dbo.tblOrderHead.intCustomerNo =
dbo.tblCustomer.intCustomerNo
AND dbo.tblOrderHead.intOrderNo = #intDocumentNo
Solution
Change the procedure to this (suggestion below might work as well, but I have not yet tested):
UPDATE dbo.tblOrderHead
SET dbo.tblOrderHead.intCustomerNo = #intCustomerNo
WHERE dbo.tblOrderHead.intOrderNo = #intDocumentNo;
UPDATE dbo.tblOrderHead
SET dbo.tblOrderHead.intPaymentCode = dbo.tblCustomer.intPaymentCode,
dbo.tblOrderHead.txtDeliveryCode = dbo.tblCustomer.txtDeliveryCode,
dbo.tblOrderHead.txtRegionCode = dbo.tblCustomer.txtRegionCode,
dbo.tblOrderHead.txtCurrencyCode = dbo.tblCustomer.txtCurrencyCode,
dbo.tblOrderHead.txtLanguageCode = dbo.tblCustomer.txtLanguageCode
FROM dbo.tblOrderHead
INNER JOIN dbo.tblCustomer ON dbo.tblOrderHead.intCustomerNo =
dbo.tblCustomer.intCustomerNo
AND dbo.tblOrderHead.intOrderNo = #intDocumentNo
Try using SET NOCOUNT OFF in your SQL sp.
When an update or insert runs, it returns information on the amount of rows affected (like when you run in SSMS).
When an adodb.command recieves this information it stops executing, hence only executing your first statement.
You could try creating Parameter objects, then appending them to the Parameters collection.
The following is untested.
Private Sub intCustomerNo_Click()
Dim cmdCommand As New ADODB.Command
Dim paramDocNo as ADODB.Parameter
Dim paramCustNo as ADODB.Parameter
Set paramDocNo = cmdCommand.CreateParameter("#intDocumentNo", adInteger, adParamInput)
Set paramCustNo = cmdCommand.CreateParameter("#intCustomerNo", adInteger, adParamInput)
cmdCommand.Parameters.Append paramDocNo
cmdCommand.Parameters.Append paramCustNo
paramDocNo.Value = Forms![frmSalesOrder].[IntOrderNo]
paramCustNo.Value = Me![intCustomerNo]
With cmdCommand
.ActiveConnection = CurrentProject.Connection
.CommandType = adCmdStoredProc
.CommandText = "uspSelectCustomer"
.Execute
End With
DoCmd.Close
Forms![frmSalesOrder].Requery
End Sub
Could it possibly but you not declaring the parameters and it using an old cached copy? Personally when I issue parameters I use something like this. I’m not saying that is what is causing the problem but try it this way and see if it helps
Set cmd = New ADODB.Command
With cmd
.CommandText = "sptblTest_answers_UPSERT"
.CommandType = adCmdStoredProc
.ActiveConnection = dbCon
.Parameters.Append .CreateParameter("#Answer_ID", adInteger, adParamInput, , Me.txtAnswer_ID)
.Parameters.Append .CreateParameter("#Question_ID", adInteger, adParamInput, , Me.txtQuestion_ID)
.Parameters.Append .CreateParameter("#Answer_number", adTinyInt, adParamInput, , Me.txtAnswer_number)
.Execute
End with