Call stored procedure with an output parameter using ExecuteDataset - mysql

I need to call a stored procedure in a MySQL (5.7) database from a VB.NET application. Said procedure makes a SELECT statement at the end which I need to retrieve in my app as a DataSet. Previously it worked fine, but I added an output parameter to the stored procedure, and I get the following error when I call it:
{"Incorrect number of arguments for PROCEDURE my_database.SP_MY_PROCEDURE; expected 3, got
2"}
Here's my current VB.NET code:
Public Shared Function CallStoredProcedure(ByVal stringParam As String,
ByVal intParam As Integer) As DataSet
Try
Dim ds As New DataSet
Dim params As New List(Of MySqlParameter)
Dim arrayParams() As MySqlParameter
params.Add(New MySqlParameter("#param1", stringParam))
params.Add(New MySqlParameter("#param2", intParam))
arrayParams = params.ToArray()
ds = MySqlHelper.ExecuteDataset(connString,
"CALL SP_MY_PROCEDURE(#param1, #param2);",
arrayParams)
Return ds
Catch objException As Exception
Return Nothing
End Try
End Function
I tried adding a third parameter to the CALL statement in the string, like this:
ds = MySqlHelper.ExecuteDataset(connString,
"CALL SP_MY_PROCEDURE(#param1, #param2, param3);",
arrayParams)
but that just returns a different error:
{"OUT or INOUT argument 3 for routine gw_my_database.SP_MY_PROCEDURE
is not a variable or NEW pseudo-variable in BEFORE trigger"}
How can I accomplish this? Maybe ExecuteDataset can't be used with output parameters; at least I haven't found examples or anything. It that's the case, what's a good alternative?

You can definitely use ExecuteDataset() with output parameter. You need to make sure all the mandatory parameters are added to your parameter list including output parameter. You need to confirm the type and size where applicable and set the direction of each parameter correctly. After executing ExecuteDataset() you can retrieve the value from our output parameter.
I could not see adding the third parameter in your code (or perhaps you haven't showed that part), setting direction/type/size.

Related

Why am I getting error 3078 on my SELECT query?

I've started to try and write some VBA to execute some queries and I've got stuck at the first hurdle. This is giving an error 3078 which apparently means it can't find the table or query. The table definitely exists and is spelt properly. Indeed the SQL runs fine - I tested it. What am I doing wrong?
Public Function Tester()
str_tbl = "tblGames_atp"
str_mkvrec = "SELECT * FROM " & str_tbl
dbl_fs_pct = DSum("FS", str_mkvrec)
End Function
Cannot reference SQL statement in domain aggregate function, not even a variable set to that statement. Must reference table or query object name. Could reference variable with name string but variable not really needed in this code. If you want function to return a value to calling source, then need to set function value.
Public Function Tester()
Tester = DSum("FS", "tblGames_atp")
End Function

SimpleJdbcCall for MySql Function yields "Can't set IN parameter for return value of stored function call"

Using the example from the Spring docs, I'm trying to return a value from a mySQL function. I keep getting the error Can't set IN parameter for return value of stored function call;.
I created a mySQL function that works fine (ran in MySQL Workbench). I've written a SimpleJdbcCall statement, set up the parameters as per Spring docs example but consistently get this error. If I turn the function into a procedure, the code works, I just have to retrieve the return value from the result set.
I used https://docs.spring.io/spring/docs/3.0.0.M3/reference/html/ch13s05.html, section 13.5.8 as reference.
CREATE FUNCTION `ScenarioRegistration`(
environment VARCHAR(45),
username VARCHAR(15),
scenario_name VARCHAR(45)) RETURNS int(11)
A couple of SELECT statements followed by an INSERT then
RETURN scenario_id; // The inserted id
Java code:
SimpleJdbcCall simpleJdbcCall = new SimpleJdbcCall(getJdbcTemplate())
.withFunctionName("ScenarioRegistration")
.withoutProcedureColumnMetaDataAccess();
simpleJdbcCall.addDeclaredParameter(new SqlParameter("environment"
,Types.VARCHAR));
simpleJdbcCall.addDeclaredParameter(new SqlParameter("username"
,Types.VARCHAR));
simpleJdbcCall.addDeclaredParameter(new SqlParameter("scenario_name"
,Types.VARCHAR));
SqlParameterSource parameters = new MapSqlParameterSource()
.addValue("environment", environment)
.addValue("username", username)
.addValue("scenario_name", scenario);
simpleJdbcCall.setReturnValueRequired(true);
Integer scenario_id = simpleJdbcCall.executeFunction(
Integer.class, parameters);
All I want the routine to do is give me back the id of the newly inserted scenario.
What I get is:
SQL [{? = call scenarioregistration(?, ?)}]; Can't set IN parameter for return value of stored function call.
I find it interesting that it's taken my THREE input values and changed them to an output and TWO input values.
Anyone enlighten me as to the problem and how to fix it?
Thanks,
Steven.
I would refer to the latest docs here for your answer. It appears Spring is trying to infer the output because you didn't explicity specify one.
Per the docs above there are two valid approaches on calling the desired function with the SimpleJdbcCall:
Inferred Parameters
Because you've specified withoutProcedureColumnMetaDataAccess, Spring isn't going to look and see what the ins/outs are to your function. If you want it easy, just don't specify that and you should be able to do:
SqlParameterSource parameters = new MapSqlParameterSource()
.addValue("environment", environment)
.addValue("username", username)
.addValue("scenario_name", scenario);
Integer scenarioId = new SimpleJdbcCall(getJdbcTemplate())
.withFunctionName("ScenarioRegistration")
.executeFunction(Integer.class, parameters);
Explicit Parameters
If you want to keep withoutProcedureColumnMetaDataAccess turned off for whatever reason, you can do:
Integer scenarioId = new SimpleJdbcCall(getJdbcTemplate)
.withFunctionName("ScenarioRegistration")
.withoutProcedureColumnMetaDataAccess()
.useInParameterNames("environment", "username", "scenario_name")
.declareParameters(
new SqlOutParameter("scenario_id", Types.NUMERIC),
new SqlParameter("environment", Types.VARCHAR),
new SqlParameter("username", Types.VARCHAR),
new SqlParameter("scenario_name", Types.VARCHAR)
).executeFunction(Integer.class, parameters);
Note: It appears that order is critical in this example. The output parameter should be declared first, and the subsequent named IN parameters come last. That is, the order of the parameters ? are ordinal in [{? = call scenarioregistration(?, ?, ?)}])
Alternative NamedParameterJdbcTemplate Solution
Another way to invoke your function is via an actual JDBC call. This could hypothetically save you the grief of using the fine tuning of the SimpleJdbcCall.
Integer scenarioId = namedParameterJdbcTemplate.queryForObject(
"SELECT ScenarioRegistration(:environment, :username, :scenario_name)",
parameters,
Integer.class);

Passing a Field for Access Query to a Module Function as an Array

I am trying to write a function in a module that we take a field from a MS ACCESS query that is passed to it and return the values of the field to another field in the Query.
The code that I have written in the module is provided below.
Public Function YearAmps(ByRef array1() As Double)
Dim array2(0 To UBound(array1())) As Double
Dim I
For I = 0 To UBound(array2())
array2(I) = array1(I)
Next I
YearAmps = array2
End Function
When I put the following in the Query field: YearAmps([SN]), where YearAmps is the function that I am calling and SN is the field that I am passing to the function and run the code, I get an error.
Does anyone know where I am going wrong. Is is possible to pass a field from an Query as an array in a Module and then return that array?
Any help would be greatly appreciated.

How do I reference a Query in a Custom Function?

I am trying to create a custom function by referencing the Excel Object Model. By first going to Create/Module/Tools/References/ and selecting Microsoft Excel 15.0 Object Library (Access 2010) I was able to allow Access to bring-in the Excel Object Model. From there, I created a basic function in Acces using the Excel functions ACOS and COS. I was then able to run this function in my Query without issue. Here is the function:
Public Function myTestFunction(x As Double) As Double
myTestFunction = Excel.WorksheetFunction.Acos(Cos(50))
End Function
This test function is generally meaningless to me, but was a good first step in achieving my ultimate goal. The next step was to reference a column in my Query within the function itself. Basically, I want to replace the 50 value with each row in my Query column [LAT]. Here is what I've tested:
Public Function myTestFunction(x As Double) As Double
myTestFunction = Excel.WorksheetFunction.Acos(Cos([Queries].[Data Query]![LAT]))
End Function
After attempting to run this function in my Query I receive the following error:
Run-Time Error 424
Object Required
My question is, how can I reference the Query column [LAT] within my function?
Revise your function to operate on its x argument.
Public Function myTestFunction(x As Double) As Double
'myTestFunction = Excel.WorksheetFunction.Acos(Cos(50))
myTestFunction = Excel.WorksheetFunction.Acos(Cos(x))
End Function
Then you can use the function in a query and give it your [LAT] field value.
SELECT dq.[LAT], myTestFunction(dq.[LAT])
FROM [Data Query] AS dq;

VBA function call

Is there a way to call a function, where the call is stored in a table
**Record 1 task Function call**
124567 Email customer Call function emailcus(a,b,c,d)
434535 AddCost Call function addcost(a,b,c,d)
Cheers
Graham
Yes, you can use the Eval() function for that.
Syntax:
Dim ReturnValue As String
ReturnValue = Eval("MyFunction(1, 2)")
Note that you have to provide the exact function call including parameters.
I'm pointing this out because I'm not sure if the parameters a, b, c, d in your example are only dummy values for your example, or if you expect VBA to fill in the values of some variables named a, b, c, d automatically.
The Eval function does not do this, so if you need variable values as parameters, you would have to do something like this:
Dim ReturnValue As String
Dim EvalString As String
EvalString = "MyFunction(" & Variable1 & ", " & Variable2 & ")"
ReturnValue = Eval(EvalString )
This is a variation on the answer already given by haarrrgh, so if you find it useful be sure to upvote that one as well.
There's another way to deal with placeholders in your DB-stored function calls. First, change your data thusly:
**Record 1 task Function call**
124567 Email customer Call function emailcus([TokenA],[TokenB])
434535 AddCost Call function addcost([TokenA],[TokenB])
Note that the [SquareBrackets] are not actually required syntax in this example, just something that I tend to use in this situation. The important part is to make the parameter tokens something that doesn't appear elsewhere in the string value (including other tokens). You can use as many parameters as you need, just make sure that the calling code knows about how many are expected by each function-call string (I cut it down to shorten my following code).
Then when it's time to call your function, do this:
Dim ReturnValue As String 'or as appropriate for individual the function's return
Dim EvalString As String
EvalString = 'code to fetch from table
EvalString = Replace(EvalString, "[TokenA]", strValueA) 'strValueA passed in?
EvalString = Replace(EvalString, "[TokenB]", strValueB) 'strValueB passed in?
ReturnValue = Eval(EvalString)
In VB6, at least (so I assume it's true in VBA), Replace is faster than concatenation. I also find this more readable, but that may be because I'm used to it from using a similar technique to build SQL commands in code (using Const declarations rather than DB storage, but that would work too).
EDIT
As I reread my "finished" post just after submitting it, I realized that there's a gotcha lurking in there. Because you're doing substitution before submitting the string to Eval, these are actual values that are being put into the string, not variables. The code I presented above works fine if your parameters are numeric, but if they're String type you have to include the quotes, either in your data or in your Replace call. I prefer the former, so change your data to this:
**Record 1 task Function call**
124567 Email customer Call function emailcus('[TokenA]','[TokenB]')
434535 AddCost Call function addcost('[TokenA]','[TokenB]')
This works as tested with a Const. Stored in a DB record, you might need this instead:
**Record 1 task Function call**
124567 Email customer Call function emailcus(""[TokenA]"",""[TokenB]"")
434535 AddCost Call function addcost(""[TokenA]"",""[TokenB]"")
(which also works with a Const...).
The alternative is to leave the data as it is in my first part, & change the Replace calls:
EvalString = Replace(EvalString, "[TokenA]", """" & strValueA & """") 'strValueA passed in?
'or maybe
EvalString = Replace(EvalString, "[TokenB]", "'" & strValueB & "'") 'strValueA passed in?
A couple of other potential gotchas: These must be Functions, not Subs, and they must be declared Public in a module, not in a Form's code.