As I am new to this, I am facing some issue in executing stored procedure in oracle DB. Here is the SP which gives record as output parameter which is of type %rowtype and l_serno as input parameter which is of type Number.
Create OR Replace procedure get_product(l_serno in product.serno%type,record out product%rowtype)
is
begin
select * into record from product where serno=l_serno;
end get_product;
Using C#, I am trying to fetch the data from the SP and show it on the gridview.
OracleCommand cmd = new OracleCommand("get_product", Conn);
cmd.CommandType = CommandType.StoredProcedure;
Conn.Open();
OracleParameter input = cmd.Parameters.Add("V_SERNO", OracleType.Number);
OracleParameter output = cmd.Parameters.Add("ITEMS_CURSOR", OracleType.Cursor);
input.Direction = ParameterDirection.Input;
output.Direction = ParameterDirection.ReturnValue;
input.Value = 2;
OracleDataReader rd = cmd.ExecuteReader();
DataTable dt = new DataTable();
dt.Load(rd);
GridView1.DataSource = dt;
GridView1.DataBind();
Conn.Close();
Here I am getting error as
ORA-06550: line 1, column 24:
PLS-00306: wrong number or types of arguments in call to 'GET_PRODUCT'
ORA-06550: line 1, column 7:
Please let me know what is the wrong I am doing here.
Thanks in Advance.
Your procedure has this signature:
(l_serno in product.serno%type,record out product%rowtype)
But in your C# code you specify this:
OracleParameter output = cmd.Parameters.Add("ITEMS_CURSOR", OracleType.Cursor);
A cursor is a pointer to a result set and is different from a variable. You could change your C# code: define a class whose attributes match the projection of the PRODUCT table. Alternatively, change the stored procedure to use a ref cursor.
The second approach is probably less work (not least because you can get us to do it for you)
create or replace procedure get_product
(l_serno in product.serno%type,
record out sys_refcursor)
is
begin
open record for
select * from product
where serno=l_serno;
end get_product;
Related
Scenario
I'm working with SQL Server 2017 (not possible to change)
I'm using Visual Studio 2019 in C# console and .NET Framework 4.5 (possible to change)
I'm using ADO.NET because several years before we couldn't use Entity Framework, as the system is made to work with a stored procedure that returns at least 100k rows (possible to change)
Situation
I have an USP that returns a table that is at least 100k of rows by 20 fields. I need to add an output parameter in order to get also an ID created by the USP itself. So, the situation is that I need return a table and an ID (called ProcMonitorId). I don't know if this is even so possible (See workarounds section)
At the SQL level is seems to be so far so good:
CREATE PROCEDURE [myschema].[mystore]
#ProcMonitorId BIGINT OUTPUT
AS
BEGIN
BEGIN TRANSACTION
(...)
SELECT fields FROM myTable
SELECT #ProcMonitorId = #internalVariable
SQL execution:
And at repository layer (only relevant lines, someone were surprised for health of example):
var command = new SqlCommand("myStoreProcedure", mycon);
command.CommandType = CommandType.StoredProcedure;
SqlParameter outPutParameter = new SqlParameter();
outPutParameter.ParameterName = "#ProcMonitorId";
outPutParameter.SqlDbType = System.Data.SqlDbType.BigInt;
outPutParameter.Direction = System.Data.ParameterDirection.Output;
command.Parameters.Add(outPutParameter);
// Open connection etc-etc that works
SqlDataAdapter da = new SqlDataAdapter(command);
DataTable dt = new DataTable();
string ProcMonitorId = outPutParameter.Value.ToString();
da.Fill(dt);
Everything worked fine until the addition of the output at C# level. It returns in the line:
string ProcMonitorId = outPutParameter.Value.ToString();
it returns NullReferenceException because Value is null (that can't be) and of course, can't convert to String. I would solve this situation by adding a ? but if that's situation happens for real, I need catch it any way as error. The main idea is that Value can not be null.
As I don't have any ORM map, (and my expertise is not ADO.NET but Entity Framework) I can't understand why is null (No, is not null at SQL layer, always return a value)
Question
How can I solve this error or how can I return a BIGINT parameter and ALSO a table result?
Workarounds
As I first glance I have to solve it quickly, I made a:
SELECT 1 as type, #procID as procid, null as data1, null as data2
UNION ALL
SELECT 2 as type, null as procid, data1, data2
in order to simulate a "header" and "data" rows on one single table.
But I don't like this solution and is not very elegant and flexible. I've to parse the header every time.
Thanks in advance and please comment anything, tip, help, workaround, I will be glade to update my answer if more information is needed.
Also I can make my Framework to .NET Core or change to Entity Framework. That I can't change is my SQL version
Update #2
No changes in SQL - Still working as screenshot
In C# - Hangs out for ever
SqlConnection connection = new SqlConnection(ConfigurationManager.AppSettings["DbConnection"]);
connection.Open();
var command = new SqlCommand("myUSP", connection);
command.CommandType = CommandType.StoredProcedure;
command.CommandTimeout = Convert.ToInt16(ConfigurationManager.AppSettings["DataBaseTimeOut"]);
if (connection.State != ConnectionState.Open)
{
connection.Open();
}
SqlParameter r = command.Parameters.Add("#ProcMonitorId", SqlDbType.BigInt);
r.Direction = ParameterDirection.Output;
DataTable dt = new DataTable();
using (var rdr = command.ExecuteReader())
{
dt.Load(rdr);
long id = (long)r.Value;
}
SqlDataAdapter da = new SqlDataAdapter(command);
da.Fill(dt);
The parameter value won't be available until after you consume the resultset, eg
var cmd0 = new SqlCommand("create or alter procedure pFoo #id int output as begin select * from sys.objects; set #id = 12; end", con);
cmd0.ExecuteNonQuery();
var cmd = new SqlCommand("pFoo", con);
cmd.CommandType = CommandType.StoredProcedure;
var p1 = cmd.Parameters.Add("#id", SqlDbType.Int);
p1.Direction = ParameterDirection.Output;
var dt = new DataTable();
using (var rdr = cmd.ExecuteReader())
{
dt.Load(rdr);
var id = (int)p1.Value;
}
You should use a Parameter with the Direction property set to ReturnValue, and, inside the sp, declare an internal variable and set it to the value you want.
Then call the RETURN statement before leaving the StoredProcedure.
As an example, see this SP:
ALTER PROCEDURE [GetTimeZoneGMT]
#TimeZone NVARCHAR(128)
AS
BEGIN
DECLARE #timeZoneNumber as INT = -20;
IF #TimeZone ='Pacific/Midway'
SET #timeZoneNumber = -11
ELSE IF #TimeZone ='Pacific/Niue'
SET #timeZoneNumber = -11
ELSE IF #TimeZone ='Pacific/Pago_Pago'
SET #timeZoneNumber = -11
SELECT 1 -- or whatever you need to have as result set
RETURN #timeZoneNumber;
END
The stored procedure ends with a (bogus) SELECT statement but also has a RETURN statement with the parameter set inside the SP logic.
Now from the C# side you could call it in this way (LinqPad example)
using (var connection = new SqlConnection("Data Source=(LOCAL);Initial Catalog=LinqPADTest;Integrated Security=True;"))
{
connection.Open();
SqlCommand cmd = new SqlCommand("GetTimeZoneGMT", connection);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#TimeZone", SqlDbType.NVarChar).Value = "Asia/Kuala_Lumpur";
SqlParameter r = cmd.Parameters.Add("#p2", SqlDbType.BigInt);
r.Direction = ParameterDirection.ReturnValue;
DataTable dt = new DataTable();
dt.Load(cmd.ExecuteReader());
r.Value.Dump(); // Prints -20
dt.Dump(); // Prints a row with a single column with 1 as value
}
From C# Code, I'm trying to call a PACKAGE.PROCEDURE() from Oracle. In this simple example I should get one value from the procedure call, but all I get is error:
wrong number or types of arguments in call to 'RETURN_NUM'
The procedure is declared as follows:
PROCEDURE return_num(xNum OUT NUMBER) AS
BEGIN
xNum:= 50;
dbms_output.put_line('hello world ' || xNum);
END;
C# code:
Oraclecon.Open();
OleDbCommand myCMD = new OleDbCommand("TEST.return_num", Oraclecon);
myCMD.CommandType = CommandType.StoredProcedure;
myCMD.Parameters.Add("xNum", OleDbType.Numeric);
OleDbDataReader myReader;
myReader = myCMD.ExecuteReader();
Can some one please point out what I'm doing wrong. Then in a real scenario I would like to call a procedure that returns a set of values from a custom Type, such as:
TYPE r_interface_data IS RECORD
(
object_id VARCHAR2(16),
obj_type VARCHAR2(32)
);
TYPE t_interfase_data IS TABLE OF r_interface_data;
How can I approach that. Thanks!
UPDATE: In my particular case I ended-up doing the following approach
using (OleDbCommand cmd = new OleDbCommand("PACKAGE.procedure_name"))
{
cmd.CommandType = CommandType.StoredProcedure;
SqlManager sqlManager = new SqlManager();
return sqlManager.GetDataSet(cmd);
}
I don't think you're that far off... try this:
OracleCommand cmd = new OracleCommand("return_num", Oraclecon);
cmd.Parameters.Add(new OracleParameter("xNum", OracleDbType.Decimal,
ParameterDirection.Output));
cmd.CommandType = CommandType.StoredProcedure;
cmd.ExecuteNonQuery();
OracleDecimal d = (OracleDecimal)cmd.Parameters[0].Value;
double result = d.ToDouble();
result now contains the out parameter from the procedure.
I think your problem is you were attempting to use a DbDataReader on a stored procedure. DbDataReader is for queries.
Also, I used ODP.net -- that may or may not have contributed to your issue, that you were using Ole.
When i try to access and excute a store procedure through following C# code
Here is my C# code
OracleParameter op = null;
OracleDataReader dr = null;
OracleCommand cmd = new OracleCommand();
cmd.CommandText = "pkg_prov_index.getNextPanel";
cmd.CommandType = CommandType.StoredProcedure;
op = new OracleParameter("pCurrentPanelId", OracleDbType.Int32);
op.Direction = ParameterDirection.Input;
op.Value = masterProviderIndex.CurrentPanelId;
cmd.Parameters.Add(op);
op = new OracleParameter("pRefCursor", OracleDbType.RefCursor);
op.Direction = ParameterDirection.inputOutput;
cmd.Parameters.Add(op);
dr =cmd.ExecuteReader(); Here , it gives me an error that says ORA-06550: line 1, column 37: PLS-00201: identifier 'XYZ' must be declared at
if(dr.HasRows)
{while(dr.Read())
{
}
}
I do have the permissions to access and execute the stored procedure so no issues with that. And when I opened and saw the Stored Procedure in Sql Developer it has an input and output cursor(input cursor is there for reading so many columns as SP is just nothing but a select statement along with a where clause on a View which is written involving 5 tables together and then output cursor is for writing the output result of that SP).Thats what my understanding of cursor is ..since I'm a .Net guy not an oracle expert.Now coming back to the most important point. If you guys look in to my code for input/output cursor all I get is oracledbtype.refcursor in C# enumeration for oracledbtype but the oracle SP is using an input/ouput cursor, is this the root of my error, because I'm sending an input/output parameter as refcursor whereas oracle SP has input/output cursor not a refcursor, but refcursor is all I get when writing code using C#.
Your help will be much appreciated.
Thanks.
Before the CommandText You should try giving a name to the procedure like this:
OracleCommand cmd = new OracleCommand("getNextPanel", _conn);
The parameter OracleParameter must match exactly the parameter name(not only the type) when you call it by CommandType.StoredProcedure
Why is there cmd.Parameters.Add(op); going twice? Also, op.Direction = ParameterDirection.inputOutput; the "i" letter must be upper cased (probably typo).
The steps given is taken by a working example which I developed, following these steps: Calling Oracle stored procedure from C#?
When I want to update row with type 'date' in oracle database through asp.net C# method it gives the following error:
error:
ORA-00932: inconsistent datatypes: expected NUMBER got TIMESTAMP
code:
string query = String.Format("update mms_meetings m set m.end_date = :end_date where m.id = :id");
OracleCommand cmd = new OracleCommand("", GetDBConnection());
cmd.CommandType = CommandType.Text;
cmd.CommandText = query;
OracleParameter opId = new OracleParameter();
opId.DbType = DbType.Int32;
opId.Value = meetId;
opId.ParameterName = "id";
cmd.Parameters.Add(opId);
OracleParameter opDateEnd = new OracleParameter();
opDateEnd.DbType = DbType.DateTime;
opDateEnd.Value = dateEnd;
opDateEnd.ParameterName = "end_date";
cmd.Parameters.Add(opDateEnd);
cmd.ExecuteNonQuery();
cmd.Dispose();
CloseDBConnection();
1) You must send the exact format for your date as specified in your table column. Check default format for your date column. Like
'yyyy/MM/dd'
2) If you are using OleDb or ODBC Connection, they both use positional parameters so order of adding the parameters is very important. Try changing the order of your parameter to see if it helps.
I'm trying to write a web service that calls an oracle stored procedure. The procedure has 2 inputs - a string (company_code) and an int (customer code). It has 2 output parameters; a number (pout_addr_code) and a varchar2 (pout_descr). When I run this procedure from SQL Developer everything works fine and both values are returned as expected. When I call the procedure from my C# web service I get the error "Oracle.DataAccess.Client.OracleException: ORA-06502: PL/SQL: numeric or value error" on execution, not during the compile.
if I comment out the cmd.ExecuteNonQuery() and the return pout_addr_code.Value... lines it will run fine and output my test string.
If I remove the varchar2 output from the procedure and comment out the section adding the pout_descr parameter and commenting out the return "I returned..." it runs fine, including returning the value from the stored procedure.
So it seems to me the problem is in executing the NonQuery when the procedure has the OracleDbType.Varchar2 line. Is there some other way that this is defined when adding the output parameter in C# using the Oracle.DataAccess reference? I found this post asking about a similar error but his solution was to 'specify the max of VARCHAR2 to 32767 in C#' but I don't see how you do that.
here's my c# code:
public string AutoPmtPost(string company_code, int customer_code)
{
string oradb = "Data Source=MySource; User Id=MyID; Password=MyPassword;";
OracleConnection conn = new OracleConnection(oradb);
conn.Open();
OracleCommand cmd = new OracleCommand("USP_JEFF_TEST", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("pin_company_code", OracleDbType.Varchar2, ParameterDirection.Input).Value = company_code;
cmd.Parameters.Add("pin_customer_code", OracleDbType.Int32, ParameterDirection.Input).Value = customer_code;
OracleParameter pout_addr_code = new OracleParameter("pout_addr_code", OracleDbType.Int32);
pout_addr_code.Direction = ParameterDirection.Output;
cmd.Parameters.Add(pout_addr_code);
OracleParameter pout_descr = new OracleParameter("pout_descr", OracleDbType.Varchar2);
pout_descr.Direction = ParameterDirection.Output;
cmd.Parameters.Add(pout_descr);
cmd.ExecuteNonQuery();
//return pout_addr_code.Value.ToString();
return "I returned something";
}
Thanks.
OracleParameter pout_descr = new OracleParameter("pout_descr", OracleDbType.Varchar2);
should be
OracleParameter pout_descr = new OracleParameter("pout_descr", OracleDbType.Varchar2, 2000);