When calling a stored procedure using either ExecuteResultSet or ExecuteReader
using (DB2Connection conn = new DB2Connection(connstr))
{
conn.Open();
DB2Command cmd = conn.CreateCommand();
cmd.Transaction = conn.BeginTransaction();
DB2Parameter db2param = new DB2Parameter("#ENTERPRISE_ID_PR091", DB2Type.Char, 15);
db2param.Direction = ParameterDirection.InputOutput;
db2param.Value = enterpriseID.ToCharArray();
cmd.Parameters.Add(db2param);
//... many parameters
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "XXISCHMA.XXIPW09D";
DB2ResultSet dr = cmd.ExecuteResultSet(DB2ResultSetOptions.Scrollable);
}
In Web API the Exceptionis thrown:
SQL0035N The file "C:\Users\documents\visual studio 2013\Projects\App\Web\msg\en_US\db2nmp.xml" cannot be opened
In other Applications an Exception is thrown:
ERROR [22023] [IBM][DB2] SQL0310N SQL statement contains too many
host variables.
I don't think the exception texts are correct...
Is this by design?
In COBOL the SQL code of -310 is returned, which is "DECIMAL HOST VARIABLE OR PARAMETER number CONTAINS NON-DECIMAL DATA".
Turns out the error -310 returned to the COBOL test program was the one to look at.
So we changed the DECIMAL TO NUMBER in the COBOL stored procedure and now we get back parameters instead of an exception.
Still don't know why this was a problem only when the select does not find any records. I only look at the c# side of the world. Oy veh!
Related
After reading an interesting article online : Calling DB2 stored procedures from .NET applications
I'd like to share an issue recently encountered with a derived code :
DateTime transa_date = DateTime.ParseExact(trandate, "yyyy-MM-dd",
CultureInfo.InvariantCulture);
DB2Connection conn = new DB2Connection(MyDb2ConnectionString);
conn.Open();
try
{
// MyDb2Connection.Open();
// conn.Open();
// assume a DB2Connection conn
DB2Transaction trans = conn.BeginTransaction();
cmd = conn.CreateCommand();
procName = "MYTBLSCHEMA.TEST";
procCall = "CALL MYTBLSCHEMA.TEST(#NAME, #ADDRESS_LINE, #REGNUM, #TRANSA)";
cmd.Transaction = trans;
cmd.CommandType = System.Data.CommandType.Text;
cmd.CommandText = procCall;
// Register input-output and output parameters for the DB2Command
cmd.Parameters.Add( new DB2Parameter("#NAME", name)); #of string type
cmd.Parameters.Add( new DB2Parameter("#ADDRESS_LINE", adr)); #of string type
cmd.Parameters.Add( new DB2Parameter("#REGNUM", reg)); #of string type
cmd.Parameters.Add( new DB2Parameter("#TRANSA", transa_date)); #of date type (in DB2 table)
// Call the stored procedure
Console.WriteLine(" Call stored procedure named " + procName);
cmd.ExecuteNonQuery();
}
The above code neither generates an exception at cmd.ExecuteNonQuery() nor inserts the (expected) row into the table.
Hence, a Hope to understand through this post the rationale underlying such phenomenon.
Thanks.
N.B: Executing (manually)
CALL MYTBLSCHEMA.TEST('test', 'test_address_', 'test_num', 2021-01-01)
from the IDE does work (e.g. insert the row into the table).
DB2 version: 11.5.6.0.00000.008
I'd either remove this line:
DB2Transaction trans = conn.BeginTransaction();
Or I'd add this line at the end of the try:
trans.Commit();
As to which you'd choose; as it's a single stored procedure, unless there's some internal overriding concern within the sproc that makes sense to have a transaction to be started outside it cover it, I'd remove it. If you have, or plan to have multiple operations that must either all-succeed or all-fail, then I'd keep it/commit it..
From a table containing rfid strings (random varchar strings) I have to select a free rfid string that is not yet in used in another table, for that i use the following sql statement:
select * from (
select rfid
from rfid_col
where rfid not in (select rfid from person)
)where rownum<=1;
the sql on itself works fine but then I needed to put it in a stored procedure
create or replace
procedure getFreeRfid(rfidout out varchar2) is
begin
select * into rfidout
from (select rfid from rfid_col where rfid not in (select rfid from person)
)where rownum<=1;
end getFreeRfid;
this code works fine in oracle sql developer
to retrieve this in C# using the oracle data I use the following code
OracleCommand rfidcommand = db.connection.CreateCommand();
rfidcommand.CommandType = System.Data.CommandType.StoredProcedure;
rfidcommand.CommandText = "getFreeRfid";
rfidcommand.Parameters.Add(new OracleParameter("rfidout", OracleDbType.Varchar2)).Direction = System.Data.ParameterDirection.Output;
rfidcommand.ExecuteNonQuery();
string rfid = rfidcommand.Parameters["rfidout"].Value.ToString();
however somehow this throws:
A first chance exception of type 'Oracle.DataAccess.Client.OracleException' occurred in Oracle.DataAccess.dll
ORA-06502: PL/SQL: numeric or value error
ORA-06512: at "TIM.GETFREERFID", line 3
ORA-06512: at line 1
I have been struggling with this problem for quite some time now and this is my last solution
It is important to set size for strings when dealing with output parameters. The size sets automatically on Input parameters because it is known. I don't know if this will fix your issue but you will not get value if size not set on the output, not a full value anyway - you may get 1 character. And make sure that type in Db is indeed Varchar2
Please, try this code:
string connStr = ".........";
string result = null;
using (OracleConnection conn = new OracleConnection(connStr))
{
conn.Open();
using (OracleCommand comm = new OracleCommand("getFreeRfid", conn))
{
comm.CommandType = CommandType.StoredProcedure;
OracleParameter p = new OracleParameter("rfidout", OracleDbType.Varchar2, ParameterDirection.Output);
p.Size = 200;
comm.Parameters.Add(p);
comm.ExecuteNonQuery();
result = (string)comm.Parameters[0].Value;
}
}
Good luck!
PS: using will help you close and dispose of command and connection.
I have an oracle package with a procedure that has a in out reference cursor. My understanding is that this is pretty standard.
What I didn't like is the fact that I had to write a ton of code to just see the output. So I asked this question and it turns out I can get what I want by creating a function that wraps the procedure.
Update: Looks like I don't need the function anymore but it may be worth knowing anyway for those curious see the original question and answer updates.
Here's the function
FUNCTION GetQuestionsForPrint (user in varchar2)
RETURN MYPACKAGE.refcur_question
AS
OUTPUT MYPACKAGE.refcur_question;
BEGIN
MYPACKAGE.GETQUESTIONS(p_OUTPUT => OUTPUT,
p_USER=> USER ) ;
RETURN OUTPUT;
END;
and here's what I do to execute it in SQL Developer
var r refcursor;
exec :r := mypackage.getquestionsForPrint('OMG Ponies');
print r;
So from now on I'm probably going to add the ForPrint functions to all my procedures.
This got me thinking, maybe functions are what I want and I don't need procedures.
To test this I tried executing the function from .NET, except I can't do it. Is this really the way it is.
using (OracleConnection cnn = new OracleConnection("Data Source=Test;User Id=Test;Password=Test;"))
{
cnn.Open();
OracleCommand cmd = new OracleCommand("mypackage.getquestionsForPrint");
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.Add ( "p_USER", "OMG Ponies");
cmd.Connection = cnn;
OracleDataReader rdr = cmd.ExecuteReader();
while (rdr.Read())
{
Console.WriteLine(rdr.GetOracleValue(0));
}
Console.ReadLine();
}
So I get the error.
getquestionsForPrint is not a procedure or is undefined
I tried ExecuteScalar as well with the same result.
EDIT Taking Slider345's advice I've also tried setting the command type to text and using the following statement and I get
invalid SQL statement
mypackage.getquestionsForPrint('OMG Poinies');
and
var r refcursor; exec :r := mypackage.getquestionsForPrint('OMG Poinies');
Using Abhi's variation for the command text
select mypackage.getquestionsForPrint('OMG Poinies') from dual
resulted in
The instruction at "0x61c4aca5"
referenced memory at "0x00000ce1". The
memory could not be "read".
Am I just barking up the wrong tree?
Update
Attempting to add an output parameter doesn't help.
cmd.Parameters.Add(null, OracleDbType.RefCursor, ParameterDirection.Output);
Not sure what the name should be since its the return value of a function (I've tried null, empty string, mypackage.getquestionsForPrint) but in all cases it just results in
ORA-06550: line 1, column 7:
PLS-00306: wrong number or types of
arguments in call to
'getquestionsForPrint'
Final Edit (hopefully)
Apparently Guddie asked a similar question 3 months after I did. He got the answer which is to
Set your command text to an anonymous block
Bind a parameter to the ref cursor setting the direction to output
Call Execute non reader.
Then use your parameter
using (OracleConnection cnn = new OracleConnection("Data Source=Test;User Id=Test;Password=Test;"))
{
cnn.Open();
OracleCommand cmd = new OracleCommand("mypackage.getquestionsForPrint");
cmd.CommandType = CommandType.Text;
cmd.CommandText = "begin " +
" :refcursor1 := mypackage.getquestionsForPrint('OMG Ponies') ;" +
"end;";
cmd.Connection = cnn;
OracleDataAdapter da = new OracleDataAdapter(cmd);
cmd.ExecuteNonQuery();
Oracle.DataAccess.Types.OracleRefCursor t = (Oracle.DataAccess.Types.OracleRefCursor)cmd.Parameters[0].Value;
OracleDataReader rdr = t.GetDataReader();
while(rdr.Read())
Console.WriteLine(rdr.GetOracleValue(0));
Console.ReadLine();
}
I have not tested this with a function, but for my stored procedures. I specify the out parameter for the refCursor.
command.Parameters.Add(new OracleParameter("refcur_questions", OracleDbType.RefCursor, ParameterDirection.Output));
If you are able to get the function to work with the CommandType.Text. I wonder if you can try adding the parameter above except with the direction as:
ParameterDirection.ReturnValue
I am using Oracle.DataAccess version 2.111.6.0
I had to go up and down between the question and answers to figure out the full code that works. So I am giving the full code here that worked for me for others -
var sql = #"BEGIN :refcursor1 := mypackage.myfunction(:param1) ; end;";
using(OracleConnection con = new OracleConnection("<connection string>"))
using(OracleCommand com = new OracleCommand())
{
com.Connection = con;
con.Open();
com.Parameters.Add(":refcursor1", OracleDbType.RefCursor, ParameterDirection.Output);
com.Parameters.Add(":param1", "param");
com.CommandText = sql;
com.CommandType = CommandType.Text;
com.ExecuteNonQuery();
OracleRefCursor curr = (OracleRefCursor)com.Parameters[0].Value;
using(OracleDataReader dr = curr.GetDataReader())
{
if(dr.Read())
{
var value1 = dr.GetString(0);
var value2 = dr.GetString(1);
}
}
}
Hope it helps.
I know this is quite an old post, but since it took me so long to figure out all of the minutia involved in getting .NET to "fight nice" with Oracle, I figured I'd put this advice out there for anyone else in this sticky situation.
I frequently call Oracle stored procedures that return a REF_CURSOR in our environment (.NET 3.5 against Oracle 11g). For a function, you can indeed name the parameter anything you'd like, but then you need to set its System.Data.ParameterDirection = ParameterDirection.ReturnValue then ExecuteNonQuery against the OracleCommand object. At that point the value of that parameter will be the ref_cursor that the Oracle function returned. Just cast the value as an OracleDataReader and loop through the OracleDataReader.
I'd post the full code, but I wrote the data access layer in VB.NET years ago, and the bulk of the code consuming the data access layer (our corporate intranet) is in C#. I figured mixing languages in a single response would be the larger faux pas.
We've been given a "stored procedure" from our RPG folks that returns six data tables. Attempting to call it from .NET (C#, 3.5) using the iSeries Provider for .NET (tried using both V5R4 and V6R1), we are seeing different results based on how we call the stored proc. Here's way that we'd prefer to do it:
using (var dbConnection = new iDB2Connection("connectionString"))
{
dbConnection.Open();
using(var cmd = dbConnection.CreateCommand())
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "StoredProcName";
cmd.Parameters.Add(new iDB2Parameter("InParm1",
iDB2DbType.Varchar).Value = thing;
var ds = new DataSet();
var da = new iDB2DataAdapter(cmd);
da.Fill(ds);
}
}
Doing it this way, we get FIVE tables back in the result set. However, if we do this:
cmd.CommandType = CommandType.Text;
cmd.CommandText = "CALL StoredProcName('" + thing + "')";
We get back the expected SIX tables.
I realize that there aren't many of us sorry .NET-to-DB2 folks out here, but I'm hoping someone has seen this before.
TIA.
Look into the LibraryList (and maybe the Naming) property of your connection string. When you use CommandType.StoredProcedure it could be executing the stored procedure right from the SQL database library. When you use CommandType.Text it searches the library list to find the stored procedure. You end up running different versions of the stored procedure from different libraries which gives you different results.
I'm calling the code below.
On the line (IDataReader dr = cmd.ExecuteReader()) sql barfs with an Incorrect syntax near 'CompanyUpdate'.
using (SqlCommand cmd = new SqlCommand("CompanyUpdate"))
{
cmd.Parameters.Add("#CompanyID",SqlDbType.Int);
cmd.Parameters.Add("#Description",SqlDbType.VarChar,50);
cmd.Parameters["#CompanyID"].Value = companyid;
cmd.Parameters["#Description"].Value = description;
SqlConnection cn = new SqlConnection("Data Source=[datasource];Initial Catalog=dotNext;User ID=[user];Password=[password];Pooling=True;Application Name=dotNext");
cn.Open();
cmd.Connection = cn;
using (IDataReader dr = cmd.ExecuteReader())
{
if (dr.Read())
{
this.CompanyID = dr.GetInt32(0);
}
}
}
I had a look at sqlprofiler and noticed the following:
exec sp_executesql N'CompanyUpdate',N'#CompanyID int,#Description varchar(50)',#CompanyID=56,#Description='APC'
Its wrapping my command wit a sp_executesql. All my other sql commands are just executed with no issues.
So my question is two fold:
1. Why is it using sp_executesql?
2. What am I doing wrong?
Details: sql2005, c#, vs2005
I notice that you've not set the CommandType to StoredProcedure... I don't know if that's the cause of your problem or not:
cmd.CommandType = CommandType.StoredProcedure;
I've done this so many times myself I can't count.
Tip to trigger your memory when this throws exceptions next time:
Have SQL Query Profiler open while you're running your app. When each command executes, it shows the SQL generated and run on the server side. If the SQL generated begins with sp_executesql followed by your query then it's being run as a regular query - i.e. cmd.CommandType = CommandType.Text, if it starts with exec, chances are it's run as a stored proc. Make sure you're getting the correct SQL generated for the type of query you're trying to run.