Here is my stored procedure:
[dbo].[DFW_Completed_Safety] (
#StartDate VARCHAR(10),
#Station VARCHAR(50),
#EmployeeID INT)
When I code the following:
SqlDataAdapter daAC_CSM = new SqlDataAdapter();
DataSet dsAC_CSM = new DataSet();
try
{
using (SqlConnection sqlConnection = new SqlConnection(connectionString))
{
sqlCmd = new SqlCommand();
sqlCmd.CommandType = CommandType.StoredProcedure;
sqlCmd.Connection = sqlConnection;
sqlCmd.CommandTimeout = 0;
sqlCmd.CommandText = "DFW_Completed_Safety";
sqlCmd.Parameters.AddWithValue("#StartDate", startdate);
sqlCmd.Parameters.AddWithValue("#Station", station);
sqlCmd.Parameters.AddWithValue("#EmployeeID", "0");
daAC_CSM.SelectCommand = sqlCmd;
daAC_CSM.Fill(dsAC_CSM);
}
return dsAC_CSM;
}
catch (Exception)
{
throw;
}
it throws the Exception: EmployeeID is received as a varchar.
Conversion failed when converting the varchar value 'd ' to data type int.
Things I tried:
1- Many others post on StackOverflow suggested that Convert.ToInt32(0); would do it. Since 0 is an Int32 by default, this isn't a solution.
2- Changing the method to receive varchar (send "0") and it doesn't work too.
Thanks for any ideas! (would be greater to keep the method signature to Int).
UPDATE: The question isn't answered yet, since changing my stored procedure to varchar didn't make it.. Any ideas?
Please rewrite your code like this:
try
{
sqlCon = new SqlConnection(connectionString);
sqlCmd = new SqlCommand();
sqlCmd.CommandType = CommandType.StoredProcedure;
SqlDataAdapter daAC_CSM = new SqlDataAdapter();
DataSet dsAC_CSM = new DataSet();
sqlCmd.Connection = sqlCon;
sqlCmd.CommandTimeout = 0;
sqlCmd.CommandText = "DFW_Completed_Safety";
sqlCmd.Parameters.AddWithValue("#StartDate", startdate); //Using "#"
sqlCmd.Parameters.AddWithValue("#Station", station); //Using "#"
sqlCmd.Parameters.AddWithValue("#EmployeeID", 0); //Using "#"
foreach(SqlParameter p in sqlCmd.Parameters){
//Will print Name, Type and Value
System.Diagnostics.Trace.WriteLine("Name:" + p.ParameterName + "Type: " + p.DbType+" Value: "+p.Value);
}
sqlCon.Open();
daAC_CSM.SelectCommand = sqlCmd;
daAC_CSM.Fill(dsAC_CSM);
sqlCon.Close();
return dsAC_CSM;
}
catch (Exception ex)
{
throw ex;
}
What does it print? What error do you get?
When you run your procedure from SSMS you will most likely get the same error, as the error is most likely derived from the body of your procedure, rather than how you are calling it. If you have a value 'd ' in a column in the table that you're querying from - and you are comparing that column to an integer type, then you will receive that error. Also, a couple of asides:
You should be putting your SqlCommand and SqlConnection instances in a using clause or disposing of them manually since they are IDisposable.
You probably don't want throw ex in your catch block - you probably just want throw. By using throw ex you mess up the stack trace that was available in the original exception.
Finally it wasn't the first line. The FormName is a field that stores the FormID. The programmer that was here before was probably a noob or changed the Column datatype to int, making all queries not to work. Thanks anyways #Matt_Whitfield & #Luxspes. By the way Luxpes, you were right, it was written line 1 even on SSMS, but I did it using the same:
EXEC #return_value = [dbo].[DFW_Completed_Safety]
#StartDate = N'07-18-2012',
#Station = N'YHZ',
#EmployeeID = 0
And by doing Print #SqlStatement, I was able to copy & paste in a new Query and see that it was the Form*Name* that was an Int. Who knew that a Name could be an Int?
Related
I have a mystery with a stored procedure that I'm calling from code behind(C#). I am baffled because I have added watchpoints my code on the C# side and everything seems to be having the values that they should be going into the call to the stored procedure however, the procedure runs without any errors that I can tell and yet my table doesn't get updated with the values that I feel they should.
The SP gets three values passed to it.
Record ID (#Record_ID), Column to update (#UpdColumn), and the value to place in that column (#UpdValue).
Here is my SP that I am calling:
ALTER PROCEDURE [dbo].[Single_Col_Update]
-- Add the parameters for the stored procedure here
#Record_ID INT,
#UpdColumn CHAR,
#UpdValue NVARCHAR
AS
BEGIN
SET NOCOUNT ON;
IF #UpdColumn = 'TicketNumber'
UPDATE dbo.csr_refdata_ip360_HostVulnerabilityCSV
SET TicketNumber = #UpdValue
WHERE RecID = #Record_ID;
IF #UpdColumn = 'TicketClosed'
UPDATE dbo.csr_refdata_ip360_HostVulnerabilityCSV
SET TicketClosed = #UpdValue
WHERE RecID = #Record_ID;
IF #UpdColumn = 'Notes'
UPDATE dbo.csr_refdata_ip360_HostVulnerabilityCSV
SET Notes = #UpdValue
WHERE RecID = #Record_ID;
IF #UpdColumn = 'Exception_ID'
UPDATE dbo.csr_refdata_ip360_HostVulnerabilityCSV
SET ExceptionID = #UpdValue
WHERE RecID = #Record_ID;
END
Here is the code segment calling the SP:
foreach (string record in recordnumber)
{
SqlConnection con = new SqlConnection("Data Source=MyDataSource");
SqlCommand cmd = new SqlCommand();
cmd.CommandText = "Single_Col_Update";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Connection = con;
cmd.Parameters.AddWithValue("#Record_ID", Convert.ToInt32(record));
cmd.Parameters.AddWithValue("#UpdColumn", Session["UpdColumn"]);
cmd.Parameters.AddWithValue("#UpdValue", Session["UpdValue"]);
con.Open();
cmd.ExecuteNonQuery();
con.Close();
}
Since all the variables are right, I'm not sure why this isn't updating. Hoping some of you may see an error here.
UPDATED 5/19/2017 1:40PM Central -
Steve,
I attempted to implement the call as you prescribed below. I only made to variations to what you provided:
'cmd.Parameters.Add("#UpdValue", SqlDbType.NVarChar, 1024);' // instead of 255 because the column I'm feeding there is an NVarChar(MAX) I will likely have to go back and modify this to be greater than 1024. There didn't appear to be a MAX value that I could put in there so for testing the 1024 will suffice.
omitted the 'transaction.Rollback();' // I kept red lining on the word 'transaction' and despite what I tried I couldn't get it to validate it.
Bottom line is that after implementing the code below the results were exactly the same as before. The code executed without reporting any errors either via the Consol.Write I added or through the VS 2017 IDE.
SqlTransaction transaction;
try
{
using (SqlConnection con = new SqlConnection("Data Source=MyDataSource"))
using (SqlCommand cmd = new SqlCommand("Single_Col_Update", con))
{
con.Open();
transaction = con.BeginTransaction();
cmd.Transaction = transaction;
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#Record_ID", SqlDbType.Int);
cmd.Parameters.Add("#UpdColumn", SqlDbType.NVarChar, 255);
cmd.Parameters.Add("#UpdValue", SqlDbType.NVarChar, 1024);
foreach (string record in recordnumber)
{
cmd.Parameters["#Record_ID"].Value = Convert.ToInt32(record);
cmd.Parameters["#UpdColumn"].Value = Session["UpdColumn"].ToString();
cmd.Parameters["#UpdValue"].Value = Session["UpdValue"].ToString();
cmd.ExecuteNonQuery();
}
transaction.Commit();
}
}
catch (Exception ex)
{
Console.Write(ex.ToString());
}
So I'm still where I was, but I have taken notice of what you shared and I concur with all you stated. I hadn't noticed that I was opening and closing the connection there and was not aware of other things you had shared.
However the quandary remains!
Update 05/22/2017 10:45AM Central time:
I realized that I was trying to stuff NVarchar type into to a Varchar type in my stored procedure. Once corrected the modifications that I made based on Steve's feedback worked just fine. I haven't tried it but I'm assuming that what I had to begin with would have worked if the types had matched to begin with, but Steve's example is cleaner so I am not even going back to test the old way. Thanks again Steve!
The problem is in the declaration of this parameter
#UpdColumn CHAR,
in this way the Stored Procedure expects a SINGLE char, not a string.
Thus all the following if statements are false and nothing will be updated
Change it to
#UpdColumn NVARCHAR(255)
The same is true for the #UpdValue parameter. Again, only a single char is received by the stored procedure. Doesn't matter if you pass a whole string.
If you don't specify the size of the NVARCHAR or CHAR parameters the database engine will use only the first char of the passed value.
I want also to underline the comment above from Alex K. While it should not give you a lot of gain it is preferable to open the connection and create the command with the parameters outside the loop. Inside the loop just change the parameters values and execute the sp
SqlTransaction transaction;
try
{
using(SqlConnection con = new SqlConnection(.....))
using(SqlCommand cmd = new SqlCommand("Single_Col_Update", con))
{
con.Open();
transaction = con.BeginTransaction())
cmd.Transaction = transaction;
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#Record_ID", SqlDbType.Int);
cmd.Parameters.Add("#UpdColumn", SqlDbType.NVarChar, 255);
cmd.Parameters.Add("#UpdValue", SqlDbType.NVarChar, 255);
foreach (string record in recordnumber)
{
cmd.Parameters["#Record_ID"].Value = Convert.ToInt32(record));
cmd.Parameters["#UpdColumn"].Value = Session["UpdColumn"].ToString();
cmd.Parameters["#UpdValue"].Value = Session["UpdValue"].ToString();
cmd.ExecuteNonQuery();
}
transaction.Commit();
}
}
catch(Exception ex)
{
// show a message to your users
transaction.Rollback();
}
I have also added all your loop inside a transaction to confirm all the inserts as a whole or reject all in case of errors.
CHAR should only be used when a column is a fixed length. When you use it with varying length strings, the results will be usually not what you expect because the parameter/column will be padded with spaces which is why your IF statements are failing.
Don't use the CHAR type for #UpdColumn. Use NVARCHAR instead for this column and also it's a good practice to specify a length for both this parameter and the UpdValue parameter in your stored procedure and then match this closely when calling the stored procedure from your C# code.
I have a stored procedure that correctly returns records when I call it from a SSMS query.
Here is the stored procedure:
CREATE PROCEDURE [dbo].[q_CheckRecords]
#ItemIDS AS VARCHAR(40)
AS
BEGIN
SET NOCOUNT ON
SELECT *
FROM q_Warehouse80_OOS_ItemsNeedingNotification
WHERE item_id = #ItemIDS
END
Calling this from a SSMS query like this:
exec [q_CheckOOSWarehouse80ItemsNeedingNotification] 'B30-R10000-B001'
It correctly returns a row, however when I use this C# code to call the stored procedure, I never get any rows returned.
SqlCommand cmd = null;
SqlDataReader myReader = null;
System.Data.SqlClient.SqlConnection conn = null;
conn = new System.Data.SqlClient.SqlConnection("Data Source=" + sSessionServer + ";database=" + sSessionDatabase + "; Integrated Security=SSPI");
String SQL = "[q_CheckOOSWarehouse80ItemsNeedingNotification]";
cmd = new SqlCommand();
cmd.CommandText = SQL;
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Connection = conn;
cmd.Parameters.Add("#ItemIDS", SqlDbType.VarChar).Value = ItemsToBeChecked;
conn.Open();
myReader = cmd.ExecuteReader();
// check to see if any rows were returned.
if (myReader.HasRows)
{
while (myReader.Read())
{
// code to read fields in returned rows here
}
}
conn.Close();
It appears to be a problem with how C# defines the datatype being passed to the stored procedure, but I haven't found any information online on how to solve this problem.
If I were to changed the stored procedure so it's "hard coded"
#ItemIDS AS VARCHAR(40)
AS
BEGIN
SET NOCOUNT ON
select * from q_Warehouse80_OOS_ItemsNeedingNotification where item_id = 'B30-R10000-B001'
END
then the C# call to it correctly indicates that a row was "found".
Any help would be greatly appreciated.
When you don't specify the length of a varChar sql treats it as length 1.
cmd.Parameters.Add("#ItemIDS", SqlDbType.VarChar).Value = ItemsToBeChecked;
Your variable ItemsToBeChecked will be truncated, and I suspect there is nothing matching in your database with just the first character of that value.
Specify the length of the varchar
cmd.Parameters.Add("#ItemIDS", SqlDbType.VarChar, 40).Value = ItemsToBeChecked;
You can verify this is the case by putting a profiler on sql, and executing your c#. You will see the value passed to the #ItemIDS parameter is only 1 character long.
The issue you are facing is because you are not calling your stored procedure in your C# Code.
Stored procedure executes fine if executed in SQL Server Management Studio.
In C# (Winforms) I have the following code:
InsertWarning.Parameters.AddWithValue("#idUser", userIDAuth);
InsertWarning.Parameters.AddWithValue("#idPass", idPass);
if (Privileged)
MessageWarning += " gave you privileged access to note " + Description;
else
MessageWarning += " gave you access to note " + Description;
InsertWarning.Parameters.AddWithValue("#Message", MessageWarning);
InsertWarning.ExecuteNonQuery();
InsertWarning.Parameters.Clear();
When ExecuteNonQuery() runs it stops saying the #idUser has no value.
Stored procedure in C#:
SqlCommand InsertWarning = new SqlCommand("_spInsertWarnings", TeamPWSecureBD);
InsertAuths.CommandType = CommandType.StoredProcedure;
Stored procedure in SQL:
[dbo].[_spInsertWarnings]
#idUser int, #idPass int, #Message nvarchar(MAX)
AS
INSERT INTO Warnings
VALUES(#idUser, #idPass, #Message)
using (SqlConnection con = new SqlConnection(dc.Con))
{
using (SqlCommand cmd = new SqlCommand("_spInsertwarnings", con))
{
cmd.CommandType = CommandType.StoredProcedure;
//Please Make SqlDataType as per your Sql ColumnType
cmd.Parameters.Add("#idUser", SqlDbType.VarChar).Value = userIDAuth;
cmd.Parameters.Add("#idPass", SqlDbType.VarChar).Value = idPass;
con.Open();
cmd.ExecuteNonQuery();
}
}
The question in this post looks similar to yours:
Stored procedure or function expects parameter which was not supplied
Have you tried using the .Parameters.Add("fieldname", type, value) instead? I'm wondering if even though you are seeing the value 8 in a debug session, it's not being recognized when you do a stored procedure call.
Thinking about this again, my guess is you're missing a different parameter than #idUser, and that parameter does not have a default value assigned. Sometimes SQL Server reports the wrong name back for a parameter missing a value.
Look at your proc header and confirm that you're passing all the required parameters that the proc expects, or that you have sensible defaults assigned for the ones you don't always want to pass.
I guess this might work, i have posted the code from where you are adding.
InsertWarning.Parameters.Add("#idUser", SqlDbType.Int);
InsertWarning.Parameters["#idUser"].Value = userIDAuth;
InsertWarning.Parameters.AddWithValue("#idPass", idPass);
try
{
connection.Open();
InsertWarning.ExecuteNonQuery()
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
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.
I have a oracle procedure which should return a concatenated string of all the parameters
create or replace procedure tin_builder (type in varchar2,
tin_serial in number, rand_digit in varchar2, tin out varchar2 ) is
BEGIN
tin := type || TO_CHAR(tin_serial) || rand_digit ;
END
Now i want to call the procedure from visual studio 2008 (C# code)
public void TinBuilder(string type, long tin_serial, string rand_digit)
{
OracleConnection connection = new OracleConnection("Data Source=xe;User ID=system;Password=******;");
OracleCommand cmd = new OracleCommand();
cmd.Connection = connection;
cmd.CommandText = "tin_builder";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("type", OracleDbType.Varchar2).Value = type;
cmd.Parameters.Add("tin_serial", OracleDbType.Decimal).Value = tin_serial;
cmd.Parameters.Add("rand_digit", OracleDbType.Varchar2).Value = rand_digit;
cmd.Parameters.Add("tin", OracleDbType.Varchar2).Direction = ParameterDirection.ReturnValue;
try
{
connection.Open();
cmd.ExecuteNonQuery();
TextBox1.Text = cmd.Parameters["tin"].Value.ToString();
}
catch (Exception ex)
{
}
finally
{
connection.Close();
}
}
Then called it with :
TinBuilder("1", 10000001, "37");
But it does not show any value in the text box :( . Please someone help me out.
"Type" is a reserved word in Oracle. Here's the link: http://www.cs.umbc.edu/help/oracle8/server.815/a42525/apb.htm
And as said by OMG Ponies change & try: SELECT type || TO_CHAR(tin_serial) || rand_digit INTO tin FROM DUAL;
And also make sure you always 'initiate' exception to catch these kind of errors
"Type" may be a reserved word. Are you sure the procedure compiled and is valid?
Also are you catching any error messages there, and hiding them with the catch clause? That seems like bad practice.