With this
PROCEDURE "ADD_BOOKMARK_GROUP" (
"NAME" IN VARCHAR2,
"BOOKMARK_GROUP_ID" IN NUMBER,
"STAFF_ID" IN VARCHAR2,
"MAX_NO" IN INT,
"NUMFOUND" OUT INT,
"NEW_ID" OUT NUMBER) IS
BEGIN
NEW_ID := -1;
SELECT COUNT(*) INTO NUMFOUND FROM BOOKMARK_GROUP_TABLE WHERE STAFF_ID = STAFF_ID;
IF NUMFOUND < MAX_NO THEN
INSERT INTO BOOKMARK_GROUP_TABLE (NAME, BOOKMARK_GROUP_ID, STAFF_ID) VALUES(NAME, BOOKMARK_GROUP_ID, STAFF_ID);
SELECT BGT_SEQUENCE.currval INTO NEW_ID FROM dual;
END IF;
END;
I find it interesting that if I don't add parameters in the order they were defined in, e.g.
OracleCommand cmd = new OracleCommand("ADD_BOOKMARK_GROUP", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new OracleParameter("NAME", name));
...
cmd.Parameters.Add(new OracleParameter("NEW_ID", OracleDbType.Decimal)).Direction = ParameterDirection.Output;
cmd.Parameters.Add(new OracleParameter("NUMFOUND", OracleDbType.Int32)).Direction = ParameterDirection.Output;
instead of
OracleCommand cmd = new OracleCommand("ADD_BOOKMARK_GROUP", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new OracleParameter("NAME", name));
...
cmd.Parameters.Add(new OracleParameter("NUMFOUND", OracleDbType.Int32)).Direction = ParameterDirection.Output;
cmd.Parameters.Add(new OracleParameter("NEW_ID", OracleDbType.Decimal)).Direction = ParameterDirection.Output;
The values returned by
cmd.Parameters["NEW_ID"].Value.ToString()
and
cmd.Parameters["NUMFOUND"].Value.ToString()
get swapped, although running the procedure through the VS2008 Server Explorer returns correct data.
Why is this?
You can probably set the BindByName parameter on the OracleCommand object. This works for straight SQL queries with parameters, I've not tried it with stored procedures but it would be logical...
cmd.BindByName = true;
I'm not an Oracle buff, so I can't verify - but it sounds like they are being passed by position (rather than passed by name). The moral equivelent to:
EXEC SomeProc 'Foo', 'Bar'
instead of:
EXEC SomeProc #arg1='Foo', #arg2='Bar'
This isn't hugely uncommon - for years (in the COM days) a lot of my code had to work with a pass-by-position ADODB driver.
In this case, the name that you give serves only as a local key to lookup the value from the collection collection. You can verify easily by inventing a name:
cmd.Parameters.Add(new OracleParameter("BANANA", ...
cmd.Parameters.Add(new OracleParameter("GUITAR", ...
...
cmd.Parameters["BANANA"].Value.ToString()
cmd.Parameters["GUITAR"].Value.ToString()
If the above runs without error, it is passing by position. And it they are passed by position... then simply add them in the right order ;-p And never add new parameters except at the end...
Not an answer to the question but you can use 'insert ... returning ... into ' in stead of select bgt_sequence.currval from dual, for example:
begin
insert into test (id)
values(test_seq.nextval)
returning id into p_id;
end;
See http://www.adp-gmbh.ch/ora/sql/insert_into_x_returning_y.html
Related
I would like to know the difference between these 2 notations.
First of all I have a stored procedure
CREATE PROCEDURE AddSomething( #zonename varchar(50), #desc varchar(255), #TheNewId int OUTPUT ) AS
BEGIN
INSERT INTO a_zone(zonename, descr) VALUES(#zonename, #desc)
SELECT #TheNewId = SCOPE_IDENTITY()
END
What is the difference if I add parameters in this manner
SqlCommand Cmd = new SqlCommand("AddSomething", oConn);
Cmd.CommandType = CommandType.StoredProcedure;
SqlParameter oParam1 = Cmd.Parameters.AddWithValue("#zonename", sName);
SqlParameter oParam2 = Cmd.Parameters.AddWithValue("#desc", description);
and
SqlCommand Cmd2 = new SqlCommand("AddSomething", oConn);
Cmd2.CommandType = CommandType.StoredProcedure;
cmd2.Parameters.Add("#zonename", SqlDbType.VarChar).Value = zonename.Text.Trim();
cmd2.Parameters.Add("#desc", SqlDbType.VarChar).Value = desc.Text.Trim();
Here are some explanations:
difference between command Add and AddWithValue
Dim cmd as new SqlCommand("SELECT * FROM MyTable WHERE MyDate>#TheDate",conn)
cmd.Parameters.Add("#TheDate",SqlDbType.DateTime).Value="2/1/2007"
vs
cmd.Parameters.AddWithValue("#TheDate","2/1/2007")
"Add forces the conversion from string to date as it goes into the parameter. AddWithValue would have simply passed the string on to the SQL Server.
When using Parameters.Add - the SqlDbType is known at compile time
When using Parameters.AddWithValue the method has to box and unbox the value to find out its type.
Additional benefits of the former is that Add is a bit more code safe
and will assist against SQL injection attacks , code safe in terms
that if you try to pass a value that doesn't match the SqlDb type
defined - the error will be caught in .Net code and you will not have
to wait for the round trip back.
http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.parameters.aspx
http://msdn.microsoft.com/en-us/library/yy6y35y8.aspx
Edit:
example to get an Output-Parameter:
C#
cmd.Parameters.Add(new SqlParameter("#TheNewId", SqlDbType.Int, int.MaxValue));
cmd.Parameters("#TheNewId").Direction = ParameterDirection.Output;
cmd.ExecuteNonQuery();
int theNewID = (int)cmd.Parameters("#TheNewId").Value;
VB.Net
cmd.Parameters.Add(New SqlParameter("#TheNewId", SqlDbType.Int, Int32.MaxValue))
cmd.Parameters("#TheNewId").Direction = ParameterDirection.Output
cmd.ExecuteNonQuery()
Dim theNewID As Int32 = DirectCast(cmd.Parameters("#TheNewId").Value, Int32)
When you use AddWithValue, the datatype will be worked out (as best possible) based on the types of the variables passed to the method - assuming sName and description are string variables, the params will be passed in as NVARCHAR.
I personally prefer the 2nd approach, being explicit with the data types (plus I actually specify the sizes too) so that they are guaranteed to match the sproc definition and avoid any unexpected behaviour.
I am trying to get a varchar value from SQL to use in function in C# here is how i tried to create Stored Procedure to return ,i appreciate any help.
#result
as an output value
CREATE PROCEDURE [dbo].[RolValidation]
#UserID int,
#result varchar(50) output
AS
BEGIN
Select #result=role from LogUser where UserID=#UserID
end
And here is the function in C#
if (sqlCon.State == ConnectionState.Closed)
sqlCon.Open();
SqlCommand sqlcmd = new SqlCommand("RolValidation", sqlCon);
sqlcmd.CommandType = CommandType.StoredProcedure;
sqlcmd.Parameters.AddWithValue("#UrunID", SqlDbType.Int);
sqlcmd.Parameters.Add("#result", SqlDbType.NVarChar,50).Direction = ParameterDirection.Output;
string rol = Convert.ToString(sqlcmd.Parameters["#UrunID"].Value);
sqlcmd.ExecuteNonQuery();
returnValue = rol;
It looks like that the code from your question is not the real code that you are working on so there might be other reasons why you don't get your value back.
However, judging from what I see if you modify the SP a little:
CREATE PROCEDURE [dbo].[RolValidation]
#UserID INT;
AS
BEGIN
SET NOCOUNT ON;
DECLARE #result varchar(50);
SET #result = Select role from LogUser where UserID=#UserID;
RETURN #result;
END
And in your C# code:
if (sqlCon.State == ConnectionState.Closed)
{
sqlCon.Open();
}
SqlCommand sqlcmd = new SqlCommand("RolValidation", sqlCon);
sqlcmd.CommandType = CommandType.StoredProcedure;
sqlcmd.Parameters.AddWithValue("#UserID", SqlDbType.Int);
sqlcmd.Parameters["#UserID"].Value = userID;
var rol = cmd.Parameters.Add("#result", SqlDbType.NVarChar, 50);
rol.Direction = ParameterDirection.ReturnValue
sqlcmd.ExecuteNonQuery();
returnValue = rol.Value;
Basically there were two main issues with your original code
1 - you should SET the value and then "RETURN" it. You can set it inline the SELECT query as you have done, but I kinda like this explicit approach a bit more
2 - you were trying to get the value before executing the query:
string rol = Convert.ToString(sqlcmd.Parameters["#UrunID"].Value);
sqlcmd.ExecuteNonQuery();
which is wrong, it should be the other way around - first execute the query then try to get the .Value
Other than that I'm not aware of the entire code but it's a good practice to wrap the connection and the command in using statements. If you don't have a good reason not to do that I suggest to change it.
P.S
Now I see that you are not passing the UserID value, at least in the code from the original question. So make sure to add this too, in my answer it's this row:
sqlcmd.Parameters["#UserID"].Value = userID;
I am executing a SQL Server stored procedure in C#, but both my output parameters are returned blank. But when I run this stored procedure directly in SSMS, then I get values for both parameters. I used same input order no.
Can anyone tell me what's wrong in my code? Thank you
using (SqlConnection con = new SqlConnection(CS))
{
SqlCommand cmd = new SqlCommand("wt_find_open_pick_ticket_count", con);
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#input_order_no", order_no);
cmd.Parameters.Add("#status", SqlDbType.Int).Direction = ParameterDirection.Output;
cmd.Parameters.Add("#results", SqlDbType.VarChar, 1000).Direction = ParameterDirection.Output;
con.Open();
cmd.ExecuteNonQuery();
MessageBox.Show(cmd.Parameters["#status"].Value.ToString());
MessageBox.Show(cmd.Parameters["#results"].Value.ToString());
}
ALTER procedure wt_find_open_pick_ticket_count
#input_order_no varchar(20),
#results varchar(1000) OUTPUT,
#status int OUTPUT
AS
SELECT
status =
CASE
WHEN COUNT(oe_pick_ticket.pick_ticket_no)>0 THEN 0
ELSE 1
END,
results =
CASE
WHEN COUNT(oe_pick_ticket.pick_ticket_no)> 0 THEN 'Message'
ELSE ''
END
FROM oe_pick_ticket with (nolock)
WHERE
oe_pick_ticket.order_no = #input_order_no
AND oe_pick_ticket.invoice_no IS NULL
AND oe_pick_ticket.delete_flag = 'N'
AND oe_pick_ticket.print_date > '2014-01-01'
GROUP BY oe_pick_ticket.order_no
This stored procedure returns a resultset. It does not return output parameters. In order to return output paramters, SP has to change to something like:
select
#paramout1 = val1,
#paramout2 = val2
Notice "#" that is preceding parameter names.
I have a stored procedure . The input is 'id', output 'n'.
But when I try to run it in Visual Studio , I have an error: The value for the output parameter 'n' is absent in the command execution result.
Here is my code:
int id = Convert.ToInt32(this.textBox1.Text);
PgSqlConnection con = new PgSqlConnection();
con.ConnectionString = Properties.Settings.Default.DBConnectionString;
PgSqlCommand cmd = con.CreateCommand();
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "getcountmaterials";
PgSqlParameter param = new PgSqlParameter("n", SqlDbType.Int);
param.Direction = ParameterDirection.Output;
cmd.Parameters.Add(param);
cmd.Parameters.Add(new PgSqlParameter("#id", id));
con.Open();
cmd.ExecuteNonQuery();
string kolvo = cmd.Parameters["n"].Value.ToString();
con.Close();
this.result.Text = kolvo;
Stored Procedure:
CREATE OR REPLACE FUNCTION public.getcountmaterials(id integer)
RETURNS integer AS
$BODY$
declare n integer;
begin n := (select sum(count) from materials_in_warehouses
where id_materials = id);
return n;
end;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION public.getcountmaterials(integer)
OWNER TO postgres;
I've never used dotConnect for Pg (I am, however, a huge fan of it for Oracle), so I can't verify that I have the syntax right on all of this.
That said, I think I see your core issue. The lines between functions and "stored procedures" is somewhat blurred with Postgresql.
All you really want to do is run a select on the function above. As such, I believe the following will work. I know this would work with NpgSql, and I am hopeful it will translate properly to dotConnect:
PgSqlCommand cmd = new PgSqlCommand("select getcountmaterials(:ID)", con);
cmd.Parameters.AddWithValue("ID", id);
string kolvo = cmd.ExecuteScalar().ToString();
I am using Npgsql 3.0.3 with Postgres 9.4. Here is my code in Postgres:
CREATE TABLE temp_test
(
id serial NOT NULL,
name text,
CONSTRAINT temp_test_pk PRIMARY KEY (id)
)
and the "Upsert" / merge function that returns the changed record as refcursor:
CREATE OR REPLACE FUNCTION test_save(
v_ref refcursor,
iv_id integer,
v_name character varying)
RETURNS refcursor AS
$BODY$
DECLARE
v_ref alias for $1;
v_id integer := iv_id;
BEGIN
UPDATE onepm.temp_test
SET name = v_name
WHERE id = v_id;
IF NOT FOUND THEN
INSERT INTO onepm.temp_test
(name)
VALUES
(v_name)
RETURNING id INTO v_id;
END IF;
OPEN v_ref FOR
SELECT id
, name
FROM onepm.temp_test
WHERE id = v_id;
RETURN v_ref;
END;
$BODY$
LANGUAGE plpgsql;
In my .net project I have the following function that returns a IDatareader:
public static IDataReader ExecuteReader()
{
NpgsqlConnection conn = new NpgsqlConnection(connectionString);
conn.Open();
NpgsqlTransaction _tran = conn.BeginTransaction();
NpgsqlCommand cmd = conn.CreateCommand();
cmd.CommandType = CommandType.Text;
cmd.CommandText = "SELECT onepm.test_save(#ref, #id, #name)";
NpgsqlParameter _p = new NpgsqlParameter();
_p.ParameterName = "#ref";
_p.NpgsqlDbType = NpgsqlDbType.Refcursor;
_p.NpgsqlValue = "ref";
_p.Direction = ParameterDirection.InputOutput;
cmd.Parameters.Add(_p);
cmd.Parameters.Add(new NpgsqlParameter("#id", 1));
cmd.Parameters.Add(new NpgsqlParameter("#name", "test"));
cmd.ExecuteNonQuery();
cmd.CommandText = "fetch all in \"ref\"";
cmd.CommandType = CommandType.Text;
return cmd.ExecuteReader();
}
This all works fine, I do receice the inserted or updated record in the reader, except that the data is never committed to the table - no data found in pgAdmin. If I call the same function in pgAdmin everything works fine - records are committed:
SELECT onepm.test_save('v_ref', 1, 'xxxxxx');
FETCH ALL IN "v_ref";
Thankful for any help!
Ummm, I think you need to commit the transaction you started...!
Regardless, you may also want to look at PostgreSQL 9.5's new built-in upsert functionality...