Problem with passing parameters to SQL procedure using VBA - sql

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

Related

Access "too few parameters" or crash but only before compact and repair

Bit of an odd one here, essentially I have a VBA function in Microsoft Access that takes two arguments provided and cuts the data from the existing table to a temp table, and then compares this to the latest data from an external an SQL database and then reappends the updated information.
This has worked fine for years, and has never been touched, until recently, everytime I run the function I get an error:
Run-time error '-2147217904(8004e10)':
Too few parameters. Expected 2.
However, if I manually compact and repair, or recompile the database, this error goes away and the function completes as normal. But only for that session, currently the staff that use this function have to compact and repair everytime they open the Acces front end to make the function complete. Compact and repair on close does not work.
The code is below, but again, it has worked as-is for year with no changes and works after a C&R.
Function AccCompleteOrder(LabID As String, OrderID As String) As Boolean
Dim cmd As New ADODB.Command
Dim conn As New ADODB.Connection
Dim rstPackageCount As ADODB.Recordset
Dim rstTmpData As ADODB.Recordset
Dim rstRealData As New ADODB.Recordset
Dim i As Integer
Dim params() As Variant
'set up cmd and query parameters
params = Array(LabID, OrderID)
Set conn = CurrentProject.Connection
cmd.ActiveConnection = conn
'check that packages have been added to the order in genophyle
cmd.CommandText = "qryAccCheckPackagesAdded"
cmd.CommandType = adCmdStoredProc
cmd.Parameters.Refresh
Set rstPackageCount = cmd.Execute(, params)
If (rstPackageCount("packageCount") = 0) Then
AccCompleteOrder = False
Exit Function
End If
'Move dummy records to Temp table
If TableExists("tmpTblAccDeletion") Then
DoCmd.DeleteObject acTable, "tmpTblAccDeletion"
End If
cmd.CommandText = "qryAccMoveToTemp"
cmd.CommandType = adCmdStoredProc
cmd.Parameters.Refresh
cmd.Execute , params
'delete old lines from table
cmd.CommandText = "qryAccDeleteFromWIL"
cmd.CommandType = adCmdStoredProc
cmd.Parameters.Refresh
cmd.Execute , params
'Append real data from Genophyle orders
cmd.CommandText = "qryAccAppendfrmGenophyle"
cmd.CommandType = adCmdStoredProc
cmd.Parameters.Refresh
cmd.Execute , params
'Get tempData recordset
cmd.CommandText = "qryAccSelectTmpData"
cmd.CommandType = adCmdStoredProc
cmd.Parameters.Refresh
Set rstTmpData = cmd.Execute
'Get real Data (from WIL) dataset
cmd.CommandText = "qryAccSelectRealData"
cmd.CommandType = adCmdStoredProc
cmd.Parameters.Refresh
cmd.Parameters.Append cmd.CreateParameter("LabID", adChar, , 10, LabID)
cmd.Parameters.Append cmd.CreateParameter("OrderID", adBigInt, , 10, OrderID)
rstRealData.Open cmd, , adOpenDynamic, adLockOptimistic
Do While Not rstRealData.EOF
rstRealData("Country") = rstTmpData("Country")
Do While Not rstTmpData.EOF
If (rstRealData.Fields("Platform") = rstTmpData.Fields("Platform")) Then
For i = 0 To rstRealData.Fields.Count - 1
If (IsNull(rstRealData.Fields(i)) Or rstRealData.Fields(i) = 0) Then
rstRealData.Fields(i) = rstTmpData.Fields(i)
End If
Next
End If
rstTmpData.MoveNext
Loop
rstTmpData.MoveFirst
rstRealData.Update
rstRealData.MoveNext
Loop
rstRealData.Close
'update the accessioning check table
cmd.CommandText = "qryAccUpdateAccessioningCheckOrderComplete"
cmd.Parameters.Refresh
cmd.Parameters.Append cmd.CreateParameter("LabID", adChar, , 10, LabID)
cmd.Parameters.Append cmd.CreateParameter("OrderID", adBigInt, , 10, OrderID)
cmd.Parameters.Append cmd.CreateParameter("ComDate", adChar, , 50, Format(Now(), "dd/mm/yyyy hh:MM:ss"))
cmd.Parameters.Append cmd.CreateParameter("ComCheck", adBoolean, , , True)
cmd.Execute
AccCompleteOrder = True
End Function
The debugger indicates the error is when it reaches the line
rstRealData.Open cmd, , adOpenDynamic, adLockOptimistic
This has me stumped.

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.

Parameterized Queries in VBA + ADODB + Oracle

I'm new to working with Oracle 11g and I'm having a lot of issues getting a parameterized query to work smoothly.
this code works:
Dim rs As ADODB.Recordset
Dim con As ADODB.Connection
Dim cmd As ADODB.Command
Dim prm As ADODB.Parameter
Set con = New ADODB.Connection
With con
.ConnectionString = GetConnection() '<-- the driver here is Driver={Oracle in OraClient11g_home1_32bit}
.Open
End With
Set cmd = New ADODB.Command
With cmd
Set .ActiveConnection = con
.CommandType = adCmdText
.CommandText = "SELECT * FROM MPA_LINEPLAN.REF_BRAND_SEASON_DROP WHERE DROP_ID = ?"
Set prm = .CreateParameter("dropID", adVarChar, adParamInput, 50, "P_SP19_5")
.Parameters.Append prm
Set rs = .Execute
End With
But the actual query I want to run will reference the dropID parameter several times. To get it working, I would have to add the same paramerter over and over. Tell me there's a better way? I tried the following:
With cmd
Set .ActiveConnection = con
.CommandType = adCmdText
.CommandText = "SELECT * FROM MPA_LINEPLAN.REF_BRAND_SEASON_DROP WHERE DROP_ID = :dropID"
Set prm = .CreateParameter("dropID", adVarChar, adParamInput, 50, "P_SP19_5")
.Parameters.Append prm
Set rs = .Execute
End With
But it hits unspecified error when I try to execute into the rs.
Also, assume for my particular case, that stored procs is not the best option (even though it should be the best option :-/)
edit:
The actual query is very long and in the interest of not making you hunt down all the :dropID references, I've reduced it here, but left enough to show the multiple references.
WITH
--...
DropDim AS (
SELECT DROP_ID
, DROP_NAME
, SEASON_ID
, SEASON_NAME
, BRAND_ID
, SEASON_YEAR
, 'DROP_' || substr(DROP_ID, LENGTH(DROP_ID),1) AS LP_Join_Drop
, SEASON_NAME || '_' || SEASON_YEAR AS LP_Join_Season
FROM MPA_LINEPLAN.REF_BRAND_SEASON_DROP
WHERE DROP_ID = :dropID),
--...
LYMap AS
(SELECT DC.DROP_ID
, DC.CHANNEL_ID
, BSD.SEASON_YEAR
, BSD.SEASON_NAME
, BSD.DROP_NAME
, FW.WEEKENDINGDATE AS LY_WEEKENDING_DATE
, FW.YEARWEEK AS LY_YEARWEEK
FROM MPA_LINEPLAN.REF_DROP_CHANNEL DC
INNER JOIN MPA_MASTER.FISCALWEEK FW
ON FW.YEARWEEK BETWEEN DC.LY_START_DT AND DC.LY_END_DT
INNER JOIN MPA_LINEPLAN.REF_BRAND_SEASON_DROP BSD ON BSD.DROP_ID = dc.DROP_ID
WHERE DC.DROP_ID = :dropID),
LLYMap AS
(SELECT DC.DROP_ID
, DC.CHANNEL_ID
, BSD.SEASON_YEAR
, BSD.SEASON_NAME
, BSD.DROP_NAME
, FW.WEEKENDINGDATE AS LLY_WEEKENDING_DATE
, FW.YEARWEEK AS LLY_YEARWEEK
FROM MPA_LINEPLAN.REF_DROP_CHANNEL DC
INNER JOIN MPA_MASTER.FISCALWEEK FW
ON FW.YEARWEEK BETWEEN DC.LLY_START_DT AND DC.LLY_END_DT
INNER JOIN MPA_LINEPLAN.REF_BRAND_SEASON_DROP BSD ON BSD.DROP_ID = dc.DROP_ID
WHERE DC.DROP_ID = :dropID ),
--....
Continue to use the qmarks placeholder and simply use a for loop to append same parameter object. Specifically, qmarks correspond to the position placed in query. Assuming the below query
sql = "SELECT * FROM MPA_LINEPLAN.REF_BRAND_SEASON_DROP" _
& " WHERE DROP_ID = ? AND DROP_ID2 = ? AND DROP_ID3 = ?"
With cmd
Set .ActiveConnection = con
.CommandType = adCmdText
.CommandText = sql
For i = 1 To 3 ' ADJUST TO NUMBER OF PARAMS
Set prm = .CreateParameter("prm" & i, adVarChar, adParamInput, 50, "P_SP19_5")
.Parameters.Append prm
Next i
Set rs = .Execute
End With
Alternatively, turn your query into a stored procedure (avoiding very large SQL string or text file read in VBA), then define one parameter.
Oracle
CREATE OR REPLACE PROCEDURE my_procedure_name(dropID IN VARCHAR2) IS
BEGIN
...long query using dropID (without any symbol)...
END;
/
VBA
With cmd
Set .ActiveConnection = con
.Properties("PLSQLRSet") = TRUE
.CommandType = adCmdText
.CommandText = "{CALL my_procedure_name(?)}"
Set prm = .CreateParameter("prm", adVarChar, adParamInput, 50, "P_SP19_5")
.Parameters.Append prm
Set rs = .Execute
End With
The best solve for this was simply to stop using Oracle's ODBC driver and start using Oracle's OLEDB as a provider instead.
Old connection string:
.ConnectionString = "Driver={Oracle in OraClient11g_home1_32bit};UID=MyUname;PWD=MyPW;DBQ=MyDB;"
New connection string:
.ConnectionString = "Provider=OraOLEDB.Oracle;Data Source=MyDB; User ID=MyUname;Password=MyPW;"
OraOLEDB supports named parameters, which is exactly what I was trying to get to in the first place. Now, I can reference parameter names in the SQL statement with : prefix.
With cmd
Set .ActiveConnection = con
.CommandType = adCmdText
.CommandText = "SELECT * FROM MPA_LINEPLAN.REF_BRAND_SEASON_DROP WHERE DROP_ID =:dropID"
Set prm = .CreateParameter("dropID", adVarChar, adParamInput, 50, "P_SP19_5")
.Parameters.Append prm
Set rs = .Execute
End With
This now works!
Another approach is to use a CTE to have all your scalar parameters and then join back to the table you are interested in:
-- Outer SELECT * due to limitations of ODBC drivers in VBA
SELECT *
FROM
(
WITH lu as (
SELECT ? as drop_id
FROM dual
)
)
SELECT t1.*
FROM mpa_lineplan.ref_brand_season_drop t1
CROSS JOIN lu -- could be inner join or whatever type you are interested in
WHERE t1.drop_id = lu.drop_id
)

Executing parameterised T-SQL stored procedure in Access vba

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

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.