Passing a parameter to a stored procedure - sqlparameter

I would like to pass a parameter to this stored procedure:
ALTER PROCEDURE [USP_SelectProject]
-- Add the parameters for the stored procedure here
#ProjectNumber as int
AS
BEGIN
if #ProjectNumber is null
begin
select * from tbl_projects
end
else
begin
SELECT * from tbl_projects where ProjectID = #ProjectNumber
end
End
Here is the .net code
SqlConnection PTConn = new SqlConnection(ConfigurationManager.ConnectionStrings["Project_Tracker"].ConnectionString);
SqlCommand PTCmd = new SqlCommand("USP_SelectProject", PTConn);
PTCmd.CommandType = CommandType.StoredProcedure;
SqlDataAdapter PTda = new SqlDataAdapter(PTCmd);
PTda.SelectCommand.Parameters["#ProjectNumber"].Value = DBNull.Value;
DataSet PTds = new DataSet();
PTda.Fill(PTds);
GridView1.DataSource = PTds;
GridView1.DataBind();
I am receiving the following error: An SqlParameter with ParameterName '#ProjectNumber' is not contained by this

You must Add() the parameter first.
SqlParameter p = PTda.SelectCommand.Parameters.Add("#ProjectNumber", DbType.Int);
p.Value = DBNull.Value;

Related

Querying a oracle database from .Net with date parameters

This one is destroying me....
I have an Oracle procedure as below: (this is my latest variation)
create or replace PROCEDURE MANAGEMENTFEES
(
STARTDATE IN VARCHAR2
, ENDDATE IN VARCHAR2
, cursor_ OUT sys_refcursor
) AS
BEGIN
OPEN cursor_ FOR
select PR.PRNUM
FROM MAXIMO.PR
WHERE PR.ISSUEDATE > to_date(STARTDATE) AND PR.ISSUEDATE < to_date(ENDDATE)
END MANAGEMENTFEES;
I am querying it using the following .Net code.
OracleConnection connection = getConnection();
connection.Open();
string startDate = new DateTime(2019, 1, 1).ToString("dd-MMM-yy");
string endDate = new DateTime(2019, 5, 1).ToString("dd-MMM-yy");
OracleDataAdapter da = new OracleDataAdapter();
OracleCommand cmd = new OracleCommand();
cmd.Connection = connection;
cmd.CommandText = "ORACLE.MANAGEMENTFEES";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("STARTDATE", OracleDbType.Varchar2).Value =startDate;
cmd.Parameters.Add("ENDDATE", OracleDbType.Varchar2).Value = endDate;
cmd.Parameters.Add("cursor_", OracleDbType.RefCursor).Direction = ParameterDirection.Output;
cmd.ExecuteNonQuery();
da.SelectCommand = cmd;
DataTable dt = new DataTable();
da.Fill(dt);
This gives me a blank data table but it does have the headers. SO that tells me the query just isnt getting any results. I have been changing the date parameters using multiple stackoverflow answers but I am getting no where.
If I hard code the dates in the procedure it works fine but I want to be able to pass different dates in.
Help?
UPDATE - Added code in with hardcoded dates
create or replace PROCEDURE MANAGEMENTFEES
(
STARTDATE IN DATE
, ENDDATE IN DATE
, cursor_ OUT sys_refcursor
) AS
BEGIN
OPEN cursor_ FOR
select PR.PRNUM
FROM MAXIMO.PR
WHERE PR.ISSUEDATE > '01-JAN-19' AND PR.ISSUEDATE < '01-MAY-19'
ORDER BY ISSUEDATE ASC;
END MANAGEMENTFEES;
Your procedure should be like this:
create or replace PROCEDURE MANAGEMENTFEES
(
STARTDATE IN DATE
, ENDDATE IN DATE
, cursor_ OUT sys_refcursor
) AS
BEGIN
OPEN cursor_ FOR
select PR.PRNUM
FROM MAXIMO.PR
WHERE PR.ISSUEDATE > STARTDATE AND PR.ISSUEDATE < ENDDATE
END MANAGEMENTFEES;
and the call in .NET would be this:
DateTime startDate = new DateTime(2019, 1, 1);
DateTime endDate = new DateTime(2019, 5, 1);
...
cmd.Parameters.Add("STARTDATE", OracleDbType.Date).Value = startDate;
cmd.Parameters.Add("ENDDATE", OracleDbType.Date).Value = endDate;
This is now resolved and it was caused by the use of STARTDATE and ENDDATE which are reserved by Oracle (although no error was thrown).
Changing them to v_STARTDATE and v_ENDDATE resolved the issue.

Oracle JDBC call PL/SQL procedure with input parameter a table of records

I am trying to call the following pl/sql procedure from JDBC.
create or replace PACKAGE test AS
type testrec_r is record (
val1 number,
val2 varchar2(100)
);
type testarr_t is table of testrec_r index by binary_integer;
function test_func(i_data in testarr_t, o_sum out number, o_totallength out number) return number;
END test;
This is how I tried to invoke it, but without success:
StructDescriptor recDescriptor = StructDescriptor.createDescriptor("test.testrec_r", conn);
STRUCT[] RECORDS_ARRAY = new STRUCT[2];
for (int i = 0; i < 2; i++) {
STRUCT oracle_record = new STRUCT(recDescriptor, conn, new
Object[] {i, "test"});
RECORDS_ARRAY[i] = oracle_record;
}
CallableStatement stmt = conn.prepareCall("{ call TEST.TEST_FUNC(?, ?, ?) }");
ArrayDescriptor arrayDescriptor = ArrayDescriptor.createDescriptor("TEST.TESTARR_T", conn);
ARRAY oracle_array = new ARRAY(arrayDescriptor, conn, RECORDS_ARRAY);
// Bind the input record
stmt.setArray(1, oracle_array);
stmt.registerOutParameter(2, Types.NUMERIC);
stmt.registerOutParameter(3, Types.NUMERIC);
stmt.executeUpdate();
double out1 = stmt.getDouble(2);
double out2 = stmt.getDouble(3);
return new Object[] { out1, out2 };
I just have read that oracle jdbc does not support pl/sql struct types. So, this fails with "invalid name pattern: test.testrec_r"
How can I call this procedure from Java? Ideally would be to only use a java libray/API, but as this seems almost imposible, which is the best workaround to wrap the pl/sql package in simple sql call and to invoke it?
P.S I am using Spring JDBCTemplate for database connection.
You cannot use PL/SQL types because they known to PL/SQL alone (since 12c this is no more strictly true - see UPD). Also any type created within a package is not visible by java directly.
You should create a SQL type at schema level. SQL types are visible to all and usable by all.
create or replace and compile java source named "ArrayOfRecTest" as
import java.io.*;
import java.sql.*;
import oracle.sql.*;
import oracle.jdbc.driver.*;
public class ArrayOfRecTest
{
public static void passArrayOfRec() throws SQLException
{
Connection conn = new OracleDriver().defaultConnection();
StructDescriptor sd = StructDescriptor.createDescriptor("MYREC_TYPE", conn);
ArrayDescriptor ad = ArrayDescriptor.createDescriptor("MYRECARR_TYPE", conn);
STRUCT[] recarr = new STRUCT[2];
for (int i = 0; i < 2; i++) { recarr[i] = new STRUCT(sd, conn, new Object[] {i+1, "value " + (i+1)}); }
ARRAY oracle_array = new ARRAY(ad, conn, recarr);
CallableStatement stmt = conn.prepareCall("{ ? = call testpkg.showArrOfRec(?, ?, ?) }");
stmt.registerOutParameter(1, Types.INTEGER);
stmt.setObject(2, oracle_array);
stmt.registerOutParameter(3, Types.INTEGER);
stmt.registerOutParameter(4, Types.INTEGER);
stmt.execute();
int sizeofArr = stmt.getInt(1);
int total = stmt.getInt(3);
int totalLength = stmt.getInt(4);
System.out.println("passArrayOfRec(total,len)=(" + total + "," + totalLength + ") " + sizeofArr + " records were shown");
}
}
/
create or replace force type myrec_type as object( id number, value varchar2(100));
/
create or replace type myrecarr_type as table of myrec_type;
/
create or replace package testpkg as
procedure passArrayOfRec as language java name 'ArrayOfRecTest.passArrayOfRec()' ;
function showArrOfRec(ra myrecarr_type, total out number, totallength out number) return number;
end testpkg;
/
create or replace package body testpkg as
--OP stuff
type testrec_r is record (val1 number, val2 varchar2(100));
type testarr_t is table of testrec_r index by binary_integer;
function test_func(data in testarr_t, total out number, totallength out number) return number is
begin
<<for_each>> for i in data.first..data.last loop
dbms_output.put_line('data(' || i || ')[val1,val2]=[' || data(i).val1 || ',' || data(i).val2 || ']');
total := nvl(total,0) + data(i).val1;
totallength := nvl(totallength,0) + length(data(i).val2);
end loop for_each;
return data.count;
end test_func;
--end OP stuff
function showArrOfRec(ra myrecarr_type, total out number, totallength out number) return number is
data testarr_t;
begin
for i in ra.first..ra.last loop data(i).val1 := ra(i).id; data(i).val2 := ra(i).value; end loop;
return test_func(data, total, totalLength);
end showArrOfRec;
end testpkg;
/
exec testpkg.passArrayOfRec;
Output:
data(1)[val1,val2]=[1,value 1]
data(2)[val1,val2]=[2,value 2]
passArrayOfRec(total,len)=(3,14) 2 records were shown
UPD New in 12cR1: Using PL/SQL Types

ODP.NET: Error when load data from Timesten refCursor to DataTable

I want to load data from Timesten DB to datatable, but i get error
Here is my code
Script:
create or replace FUNCTION APITT_tbl_request(prefix_db IN VARCHAR2) RETURN SYS_REFCURSOR AS
res SYS_REFCURSOR;
BEGIN
OPEN res FOR SELECT * FROM APITT_tbl_request_in;
RETURN res;
END;
C# code
conn = new OracleConnection(conf.GetAPIDatabaseConnectionString());
conn.Open();
OracleTransaction tran = conn.BeginTransaction(IsolationLevel.ReadCommitted);
OracleCommand command = new OracleCommand();
DataTable dt = new DataTable();
command = new OracleCommand(#"BEGIN :RETURNCURSOR := APITT_tbl_request(:prefix_db); END;");
command.CommandType = CommandType.Text;
command.Parameters.Add("RETURNCURSOR", OracleDbType.RefCursor, ParameterDirection.ReturnValue);
command.Parameters.Add("prefix_db", OracleDbType.Varchar2, ParameterDirection.Input).Value = prefix_db;
OracleDataAdapter da = new OracleDataAdapter(command);
da.Fill(dt);
Error in da.Fill(dt);
{System.InvalidOperationException: Operation is not valid due to the current state of the object. at Oracle.DataAccess.Client.OracleCommand.ExecuteReader
You haven't set the connection that the command uses.
With ODP.NET it's possible to have more than one connection open at the same time. If you don't specify which connection a command should use, how is ODP.NET supposed to know?
Either add the line
command.Connection = conn;
or replace the line
OracleCommand command = new OracleCommand();
with
OracleCommand command = conn.CreateCommand();

Oracle.DataAccess (ODP.NET) Array Binding optimal block size

Is there a way to calculate the optimal block size when using array binding to insert large amounts of data?
I do it like this: When my application runs a command the first time I call this function:
Public Function GetRowSize(ByVal cmd As OracleCommand) As Integer
Dim dr As OracleDataReader
dr = cmd.ExecuteReader()
Return CInt(dr.GetType.GetField("m_rowSize", Reflection.BindingFlags.Instance Or Reflection.BindingFlags.NonPublic).GetValue(dr))
End Function
The result I store in a class variable for further use.
It is executed like this:
Dim cmd As OracleCommand
Dim da As OracleDataAdapter, dt As New DataTable
Dim expectedNumberOfRows As Integer
cmd = New OracleCommand("BEGIN :res := GetData(:expectedRows); END;"), server.con)
cmd.Parameters.Add("res", OracleDbType.RefCursor, ParameterDirection.ReturnValue)
cmd.Parameters.Add("expectedRows", OracleDbType.Int32, ParameterDirection.Output)
cmd.Parameters("expectedRows").DbType = DbType.Int32
If rowSize = 0 Then rowSizes = GetRowSize(cmd)
cmd.ExecuteNonQuery()
expectedNumberOfRows = CInt(cmd.Parameters("expectedRows").Value)
cmd.FetchSize = rowSize * expectedNumberOfRows
da = New OracleDataAdapter(cmd)
da.Fill(dt)
In my PL/SQL I run EXPLAIN PLAN in order to get the estimated number of rows, it looks like this:
FUNCTION GetData(estimatedRows OUT NUMBER) RETURN SYS_REFCURSOR IS
res SYS_REFCURSOR;
BEGIN
OPEN res FOR SELECT * FROM MY_TABLE;
DELETE FROM PLAN_TABLE;
EXECUTE IMMEDIATE 'EXPLAIN PLAN FOR SELECT * FROM MY_TABLE';
SELECT CARDINALITY
INTO estimatedRows
FROM PLAN_TABLE
WHERE ID = 0;
RETURN res;
END;

Using ODP.NET to get a RECORD from PL/SQL function, without touching PL/SQL code

The title is pretty-self-explanatory: from a C# application, using ODP.NET, I'm trying to call a PL/SQL function that returns not a simple value, but a record.
Unfortunately, I'm not authorized to add or change the PL/SQL code, so attempting to wrap the function in another function that returns a different type is not an option for me.
Here is a simplified example...
PL/SQL:
CREATE OR REPLACE PACKAGE FOO_PACKAGE AS
TYPE FOO_RECORD IS RECORD (
BAR VARCHAR2(50),
BAZ VARCHAR2(50)
);
FUNCTION FOO_FUNCTION RETURN FOO_RECORD;
END;
/
CREATE OR REPLACE PACKAGE BODY FOO_PACKAGE AS
FUNCTION FOO_FUNCTION RETURN FOO_RECORD AS
R FOO_RECORD;
BEGIN
R.BAR := 'Hello bar!';
R.BAZ := 'Hello baz!';
RETURN R;
END;
END;
/
C#:
The first thing I tried was the most direct approach, but I'm at a loss how to bind the return parameter...
using (var conn = new OracleConnection(connection_string)) {
conn.Open();
using (var tran = conn.BeginTransaction()) {
using (var cmd = conn.CreateCommand()) {
cmd.CommandText = "FOO_PACKAGE.FOO_FUNCTION";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(
null,
/* What to use here? */,
ParameterDirection.ReturnValue
);
cmd.ExecuteNonQuery();
}
}
}
I have also tried some "roundabout" approaches, but they all throw exceptions:
cmd.CommandText = "SELECT * FROM FOO_PACKAGE.FOO_FUNCTION";
cmd.ExecuteReader(); // ORA-00942: table or view does not exist
cmd.CommandText = "SELECT FOO_PACKAGE.FOO_FUNCTION FROM DUAL";
cmd.ExecuteReader(); // ORA-00902: invalid datatype
cmd.CommandText = "SELECT BAR, BAZ FROM (SELECT FOO_PACKAGE.FOO_FUNCTION FROM DUAL)";
cmd.ExecuteReader(); // ORA-00904: "BAZ": invalid identifier
You need anonymous PL/SQL block to convert function result to another representation:
declare
vFooRes FOO_PACKAGE.FOO_RECORD;
vRes sys_refcursor;
begin
vFooRes := FOO_PACKAGE.FOO_FUNCTION;
open vRes for select vFooRes.BAR, vFooRes.BAZ from dual;
--:result := vRes;
end;
SQLfiddle
And execute it instead of calling stored procedure:
cmd.CommandText = "declare\n" +
" vFooRes FOO_PACKAGE.FOO_RECORD;\n" +
"begin\n" +
" vFooRes := FOO_PACKAGE.FOO_FUNCTION;\n" +
" open :result for select vFooRes.BAR, vFooRes.BAZ from dual;\n" +
"end;";
OracleParameter p = cmd.Parameters.Add(
"result",
OracleDbType.RefCursor,
DBNull.Value,
ParameterDirection.Output
);
cmd.ExecuteNonQuery();
After executing cmd you get cursor in result parameter which can be used to fill dataset:
var adapter = new OracleDataAdapter(cmd);
var data = new DataSet("FooDataSet");
adapter.Fill(data, "result", (OracleRefCursor)(p.Value));

Resources