My task is to call a stored procedure from the Oracle database.
The stored procedure is defined as follows:
CREATE OR REPLACE PROCEDURE my_st_proc
(pname IN VARCHAR2, pdate IN date, o_rc OUT sys_refcursor, o_flag OUT number);
So it takes two input arguments and returns two output arguments, one of which is a cursor.
When testing performance in pl\sql developer with the following code, it completes within 2 - 3 seconds.
DECLARE
pname varchar2(300) := 'john doe';
pdate date := to_date('01/01/1900','dd/mm/yyyy');
o_flag number;
o_data sys_refcursor;
--MyRec describes the fields, returned by the cursor
TYPE MyRec IS RECORD
(cAccount VARCHAR2(20), cBalance number, cDate date, cCurr varchar2(8));
rec MyRec;
BEGIN
my_st_proc(pname,pdate,o_data,o_flag);
dbms_output.put_line(o_flag);
LOOP
FETCH o_data INTO rec;
EXIT WHEN o_data%NOTFOUND;
dbms_output.put_line(
rec.cAccount||','||rec.cBalance||','||rec.cDate||','||rec.cCurr);
END LOOP;
close o_data;
END;
However when I call the stored procedure via ODP.Net it takes up to two seconds more to complete (3 - 5 seconds).
const string p_name = "pname";
const string p_date = "pdate";
const string p_data = "o_data";
const string p_flag = "o_flag";
using (var connection = new OracleConnection("my connection"))
{
var command = connection.CreateCommand();
command.CommandType = CommandType.StoredProcedure;
command.CommandText = "my_st_proc";
var pname = command.Parameters.Add(p_name, OracleDbType.Varchar2);
pname.Direction = ParameterDirection.Input;
var pdate = command.Parameters.Add(p_date, OracleDbType.Date);
pdate.Direction = ParameterDirection.Input;
command.Parameters.Add(p_data, OracleDbType.RefCursor).Direction =
ParameterDirection.Output;
var pflag = command.Parameters.Add(p_flag, OracleDbType.Int32);
pflag.Direction = ParameterDirection.Output;
if (command.Connection.State != ConnectionState.Open)
command.Connection.Open();
command.Parameters[p_name].Value = name;
command.Parameters[p_date].Value = date;
DateTime bdate = DateTime.Now;
command.ExecuteNonQuery();
if (((OracleDecimal)command.Parameters[p_flag].Value).ToInt32() == 1)
{
}
else
{
using (var oreader = command.ExecuteReader())
{
if (oreader != null)
{
try
{
while (oreader.Read()){ }
}
finally
{
oreader.Close();
}
}
}
}
MessageBox.Show(DateTime.Now.Subtract(bdate).ToString());
}
The most time consuming lines of code appeared to be
command.ExecuteNonQuery();
and
command.ExecuteReader()
The number of lines returned by the cursor is not more than 10 - 15, few enough and reading them through the reader does take milliseconds; so I suppose it's not the FetchSize or RowSize issue.
Is there anything I can do to improve performance with ODP.Net in this cituation?
First, you function includes opening the database. I guess in your other tool, you already did the connection and just executed the command. Depending on your database security and location, that easily takes 1-10 seconds.
I don't think this will actually gain you seconds, but you never used CommandType.StoredProcedure. Instead you built SQL yourself. Let ODP.Net worry about that. Just pass the correct comand type and the procedures name as text.
Start by making sure the execution plans are actually the same.
Work with your DBAs and ask them to capture an explain plan for both the stand alone run (aqua data studio) and your odp.net call and confirm they are in fact the same. If they are not, then that will probably explain your problem. You can then try adding "enlist=false" to your connection string but better yet have the DBA's update the statistics on the related tables, hopefully fixing the slow plan. See https://stackoverflow.com/a/14712992/852208 for more info.
I have had this same issue and it came down to oracle being less optimistic about the execution plan when a distributed transaction could be involved.
The above answer is from: https://stackoverflow.com/a/15886630/852208. This is essentially a "runs fine from X but not from Y" so it may be a duplicate but we'll see.
Related
I have a stored procedure that runs a select query through a cursor and returns the cursor (Original Select query is more complex, I have shortened it).
CREATE OR REPLACE PROCEDURE GETASSIGNEDROLES(IN V_USER_ID INTEGER) SPECIFIC GETASSIGNEDROLES DYNAMIC RESULT SETS 1 LANGUAGE SQL NOT DETERMINISTIC EXTERNAL ACTION READS SQL DATA CALLED ON NULL INPUT INHERIT SPECIAL REGISTERS
BEGIN
DECLARE TEMP_CURSOR CURSOR WITH HOLD WITH RETURN TO CLIENT FOR
SELECT DISTINCT ROLE_ID, ROLE_NAME FROM ROLE ORDER BY ROLE_NAME FOR READ ONLY WITH UR;
IF V_USER_ID IS NOT NULL AND V_USER_ID > 0 THEN
OPEN TEMP_CURSOR;
END IF;
END;
Now, I could not find any example showing how to get values from a stored procedure that is returning a cursor. I believe it is the same as for every other stored procedure containing just select statement?
Also, my database is DB2 so the syntax is a little different. So far I have the following code after watching few questions on SO. But I didn't find any question using Datatable and passing parameters in CMD, is this correct?
DataTable DT = new DataTable();
using (var connection = new DB2Connection(ConnectionString))
{
//connection.Open(); //open/close connection will be done implicitely by the DataAdapter.
using (DB2Command cmd = connection.CreateCommand())
{
cmd.CommandText = "GETASSIGNEDROLES";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new DB2Parameter(DataBaseObjects.ParamVUserID, DB2Type.Integer)).Value = userDo.User_ID;
//cmd.CommandTimeout = CommandTimeout; //wait time before terminating the attempt to execute a command and generating an error in secs
using (var da = new DB2DataAdapter(cmd))
{
da.Fill(DT);
}
DT.TableName = TableName;
return DT;
}
}
It's necessary to study all of the related/linked pages of the Db2 Knowledge Center for your Db2-version that describe C# .net common language runtime procedures.
Start here and study every linked page, make the examples work on your environment.
If your Db2-server runs on Microsoft-Windows, or if you installed the Windows-specific Db2-samples with your full data-server-client (default location is:
\program files\ibm\sqllib\samples\dotnet\cs
) then you can see the DataTable examples for C# there, and also online in the Knowledge-Center - "DbDatMap.cs" and related files and "SpClient.cs" and its dependencies
Please check why you populate DataTable DT but return DTT.
If you debug your code you should be able to iterate over rows in DT after the da.Fill(DT); completes successfully.
A consoleApp using most of your syntax works fine for me, the only variations being that I used a pre-existing connection, and supply a varchar parameter to my stored procedure instead of integer (but that cannot make a difference).
So I finally got this to working. About the open cursors, apparently that's how you do it in DB2. Cursor is left open for the client application to read from. This link helped: https://bytes.com/topic/db2/answers/465403-how-do-well-anything-db2-sql
Stored Procedure used (modified from question):
CREATE OR REPLACE PROCEDURE GETASSIGNEDROLES(IN V_USER_ID INTEGER, IN V_BUSINESS_ENTITY_ID INTEGER) SPECIFIC GETASSIGNEDROLES DYNAMIC RESULT SETS 1 LANGUAGE SQL NOT DETERMINISTIC EXTERNAL ACTION READS SQL DATA CALLED ON NULL INPUT INHERIT SPECIAL REGISTERS
BEGIN
DECLARE TEMP_CURSOR CURSOR WITH HOLD WITH RETURN TO CLIENT FOR
SELECT DISTINCT ROLE_ID, ROLE_NAME FROM ROLE ORDER BY ROLE_NAME FOR READ ONLY WITH UR;
IF V_USER_ID IS NOT NULL AND V_USER_ID > 0 THEN
OPEN TEMP_CURSOR;
END IF;
END;
Function in C# to call stored procedure and save retrieved data in a data table:
public DataTable GetAssignedRolesForUser(string ConnectionString, string TableName, User userDo)
{
//LogManager.EventLog("Inside GetAssignedRolesForUser");
try
{
DataTable DTT = new DataTable();
//See if UserID exists check is required here
using (var connection = new DB2Connection(ConnectionString))
{
//connection.Open(); //open/close connection will be done implicitely by the DataAdapter.
using (DB2Command cmd = connection.CreateCommand())
{
cmd.CommandText = DataBaseObjects.spGetAssignedRoles;
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new DB2Parameter(DataBaseObjects.ParamVUserID, DB2Type.Integer)).Value = userDo.User_ID;
cmd.Parameters.Add(new DB2Parameter(DataBaseObjects.ParamVBusinessEntityID, DB2Type.Integer)).Value = ConfigurationManager.AppSettings["BusinessEntity"];
//cmd.CommandTimeout = CommandTimeout; //wait time before terminating the attempt to execute a command and generating an error in secs
using (var da = new DB2DataAdapter(cmd))
{
da.Fill(DTT);
}
DTT.TableName = TableName;
return DTT;
}
}
}
catch (Exception)
{
//LogManager.EventLog("[Error] Inside GetAssignedRolesForUser: " + ex.Message);
throw;
}
}
Code to call the C# function:
DataTable DtRoles = <Call function here>
foreach (DataRow row in DtRoles.Rows)
{
list.Add(Convert.ToInt32(row["ROLE_ID"].ToString()));
}
Working with:
ASP.Net web-forms application
C# code not vb.net
SQL Server with hard coded test data
Note: this issue doesn't cause any errors or cause any disruption in the code, however it outputs differently from expected.
What I am trying to do is populate a Gridview using code behind file, which can be updated by the user on button click.
Code to populate:
protected void PopulateReport()
{
// create connection and add commands
SqlConnection con = new SqlConnection(GetConnectionString());
con.Open();
if(RP_SelectEmp.Text == "ALL")
{
string query1 = "SELECT RequestID, empName, RequestType, RequestDesc, RequestStartDate FROM TOR WHERE (RequestStartDate > #StartDate)" +
" AND (RequestEndDate < #EndDate) AND (granted = #State)";
SqlCommand cmd = new SqlCommand(query1, con);
// needed conversions
DateTime startD = Convert.ToDateTime(RP_FromDateSelect.Text);
DateTime endD = Convert.ToDateTime(RP_EndDateSelect.Text);
Boolean state = Convert.ToBoolean("True");
// needed parameters
cmd.Parameters.AddWithValue("#State", state);
cmd.Parameters.AddWithValue("#StartDate", startD);
cmd.Parameters.AddWithValue("#EndDate", endD);
// import into gridview
using (SqlDataReader reader = cmd.ExecuteReader())
{
if (reader.Read())
{
GridView1.DataSource = reader;
GridView1.DataBind();
}
else
{
RP_ErroField.Text = "failed to bind data (reader not read) check C# code";
}
}
con.Close();
}
}
This compiles and returns no errors but outputs:
The database table includes all the correct data types and column names:
What I have tried:
creating a static data Source and passing in the same select string from the above code (this returns the hard coded event, with the same exact input from the fields seen in the picture) - which tells me the query isn't wrong AddingDataSource,InputingData,Correct event Grabbed
I have tried changing the conversions in the code, DateTime.Parse and Convert.ToDateTime had the same result. Same can be said for bool and Boolean
I have tried the each where clause separately and got the same no data to display result.
I have debugged this if statement for 2 hrs and all the variable data is doing exactly what it should (going to the if, converting, setting the values, running the reader, and databinding)
I don't know what else to try. I would like help on an action plan to fix this; maybe I am missing something, or my approach is wrong/outdated.
This is really just a debugging exercise.
First, double-check that you haven't simply named the two date-picker controls backwards! That happens a lot.
Next: go to SSMS, and take your existing query:
SELECT RequestID, empName, RequestType, RequestDesc, RequestStartDate
FROM TOR
WHERE (RequestStartDate > #StartDate)
AND (RequestEndDate < #EndDate) AND (granted = #State)
Now; we know that you've used Convert.ToDateTime to parse the dates, and that's great. You might want to check the cultures that it is parsing to what you expect it to parse to (is 1/2/2018 the first of Feb? or the 2nd of Jan?), and when you're 100% sure what the actual date of startD and endD are, prepend these to your query using an unambiguous format (just to help us debug); do the same thing with state; for example:
DECLARE #StartDate datetime = '01 Jan 2018';
DECLARE #EndDate datetime = '03 Jan 2018';
DECLARE #State bit = 1;
or are they?
DECLARE #StartDate datetime = '01 Jan 2018';
DECLARE #EndDate datetime = '01 March 2018';
DECLARE #State bit = 1;
So now we have spoofed the parameters and you have the exact same query: run it. 99% of the time, doing this will show you what is wrong with the query. I would expect that the query in SSMS now behaves like the query from your application does. So; now go fix it!
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 list Called ListTypes that holds 10 types of products. Below the store procedure loops and gets every record with the product that is looping and it stores it in the list ListIds. This is killing my sql box since I have over 200 users executing this constantly all day.
I know is not a good architecture to loop a sql statement, but this the only way I made it work. Any ideas how I can make this without looping? Maybe a Linq statement, I never used Linq with this magnitude. Thank you.
protected void GetIds(string Type, string Sub)
{
LinkedIds.Clear();
using (SqlConnection cs = new SqlConnection(connstr))
{
for (int x = 0; x < ListTypes.Count; x++)
{
cs.Open();
SqlCommand select = new SqlCommand("spUI_LinkedIds", cs);
select.CommandType = System.Data.CommandType.StoredProcedure;
select.Parameters.AddWithValue("#Type", Type);
select.Parameters.AddWithValue("#Sub", Sub);
select.Parameters.AddWithValue("#TransId", ListTypes[x]);
SqlDataReader dr = select.ExecuteReader();
while (dr.Read())
{
ListIds.Add(Convert.ToInt32(dr["LinkedId"]));
}
cs.Close();
}
}
}
Not a full answer, but this wouldn't fit in a comment. You can at least update your existing code to be more efficient like this:
protected List<int> GetIds(string Type, string Sub, IEnumerable<int> types)
{
var result = new List<int>();
using (SqlConnection cs = new SqlConnection(connstr))
using (SqlCommand select = new SqlCommand("spUI_LinkedIds", cs))
{
select.CommandType = System.Data.CommandType.StoredProcedure;
//Don't use AddWithValue! Be explicit about your DB types
// I had to guess here. Replace with the actual types from your database
select.Parameters.Add("#Type", SqlDBType.VarChar, 10).Value = Type;
select.Parameters.Add("#Sub", SqlDbType.VarChar, 10).Value = Sub;
var TransID = select.Parameters.Add("#TransId", SqlDbType.Int);
cs.Open();
foreach(int type in types)
{
TransID.Value = type;
SqlDataReader dr = select.ExecuteReader();
while (dr.Read())
{
result.Add((int)dr["LinkedId"]);
}
}
}
return result;
}
Note that this way you only open and close the connection once. Normally in ADO.Net it's better to use a new connection and re-open it for each query. The exception is in a tight loop like this. Also, the only thing that changes inside the loop this way is the one parameter value. Finally, it's better to design methods that don't rely on other class state. This method no longer needs to know about the ListTypes and ListIds class variables, which makes it possible to (among other things) do better unit testing on the method.
Again, this isn't a full answer; it's just an incremental improvement. What you really need to do is write another stored procedure that accepts a table valued parameter, and build on the query from your existing stored procedure to JOIN with the table valued parameter, so that all of this will fit into a single SQL statement. But until you share your stored procedure code, this is about as much help as I can give you.
Besides the improvements others wrote.
You could insert your ID's into a temp table and then make one
SELECT * from WhatEverTable WHERE transid in (select transid from #tempTable)
On a MSSQL this works really fast.
When you're not using a MSSQL it could be possible that one great SQL-Select with joins is faster than a SELECT IN. You have to test these cases by your own on your DBMS.
According to your comment:
The idea is lets say I have a table and I have to get all records from the table that has this 10 types of products. How can I get all of this products? But this number is dynamic.
So... why use a stored procedure at all? Why not query the table?
//If [Type] and [Sub] arguments are external inputs - as in, they come from a user request or something - they should be sanitized. (remove or escape '\' and apostrophe signs)
//create connection
string queryTmpl = "SELECT LinkedId FROM [yourTable] WHERE [TYPE] = '{0}' AND [SUB] = '{1}' AND [TRANSID] IN ({2})";
string query = string.Format(queryTmpl, Type, Sub, string.Join(", ", ListTypes);
SqlCommand select = new SqlCommand(query, cs);
//and so forth
To use Linq-to-SQL you would need to map the table to a class. This would make the query simpler to perform.
I am trying to figure out why a stored procedure call takes seconds in a SQL server express query window, but when I run call the stored procedure in code the query TIMES OUT. We are using sql server 2008. I know it is hard to say exactly what is going on without seeing the stored procedure. I'm just hoping this is a known issue. Any guidance is much appreciated.
SQL query that calls "STORED_PROCEDURE_X" and runs in 2 seconds in SQL server express query window:
EXEC STORED_PROCEDURE_X '07/01/2010', '07/31/2010', 0, '', 'true','', 'Top 20'
Code that calls "STORED_PROCEDURE_X" and TIMES OUT:
SqlConnection connSQL = null;
SqlCommand sqlCmd = null;
SqlDataAdapter sqlDataAdpater = null;
DataTable returnData = null;
try
{
returnData = new DataTable();
connSQL = new SqlConnection(sqlConnection);
sqlCmd = new SqlCommand("STORED_PROC_X", connSQL);
if (connSQL.State == ConnectionState.Closed)
{
connSQL.Open();
}
sqlCmd.CommandType = CommandType.StoredProcedure;
sqlCmd.CommandTimeout = 600;
sqlCmd.Parameters.Add("#StartDate", SqlDbType.NVarChar).Value = "07/01/2010";
sqlCmd.Parameters.Add("#EndDate", SqlDbType.NVarChar).Value = "07/31/2010";
sqlCmd.Parameters.Add("#AuditType", SqlDbType.Int).Value = "0";
sqlCmd.Parameters.Add("#SortBy", SqlDbType.NVarChar).Value = "";
sqlCmd.Parameters.Add("#IsClaimDepartment", SqlDbType.NVarChar).Value = "true";
sqlCmd.Parameters.Add("#IdsList", SqlDbType.NVarChar).Value = "";
sqlCmd.Parameters.Add("#ReportType", SqlDbType.NVarChar).Value = "Top 20";
sqlDataAdpater = new SqlDataAdapter(sqlCmd);
sqlDataAdpater.Fill(returnData);
if (connSQL.State == ConnectionState.Open)
{
connSQL.Close();
}
return returnData;
}
catch (Exception ex)
{
LogErrorMessages("ExecuteStoredProcedure", ex.Message);
throw ex;
}
Exception Received:
System.Data.SqlClient.SqlException: Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.
So, running a stored proc that returned 30 records took me 00:00 second in the management console but when loading it in .net took me about 40 seconds, more than the default 30 sec TimeOut.
I just modify the stored proc and rerun the ALTER PROCEDURE... code without changing anything and the problem was solved instantly. I don't have more details about the source of this error, but at least it's a very quick fix
What helped me always a lot was to Add the "with recompile" Option to the Procedure.
http://technet.microsoft.com/en-us/library/ms190439.aspx
Assuming that you are passing the same parameters from your code as when you are testing in SSMS and your SSMS test is exactly the same in terms of data type usage I would have thought this is likely to be a parameter sniffing issue.
Do you have access to SQL Profiler (doesn't come with Express edition) to get both actual execution plans? If not you can follow the advice in this answer to get the plans.
Try to a) reorganize your code, and b) increase the timeout:
DataTable returnData = null;
try
{
using(SqlConnection connSQL = new SqlConnection(sqlConnection))
using(SqlCommand sqlCmd = new SqlCommand("STORED_PROC_X", connSQL))
{
sqlCmd.CommandType = CommandType.StoredProcedure;
sqlCmd.CommandTimeout = 1200;
// those two parameters should really be SqlDbType.DateTime!!
sqlCmd.Parameters.Add("#StartDate", SqlDbType.NVarChar, 25).Value = "07/01/2010";
sqlCmd.Parameters.Add("#EndDate", SqlDbType.NVarChar, 25).Value = "07/31/2010";
sqlCmd.Parameters.Add("#AuditType", SqlDbType.Int).Value = "0";
sqlCmd.Parameters.Add("#SortBy", SqlDbType.NVarChar, 50).Value = "";
// this parameter should really be SqlDbType.Bit !!
sqlCmd.Parameters.Add("#IsClaimDepartment", SqlDbType.NVarChar, 50).Value = "true";
sqlCmd.Parameters.Add("#IdsList", SqlDbType.NVarChar, 25).Value = "";
sqlCmd.Parameters.Add("#ReportType", SqlDbType.NVarChar, 25).Value = "Top 20";
SqlDataAdapter sqlDataAdpater = new SqlDataAdapter(sqlCmd);
returnData = new DataTable();
sqlDataAdpater.Fill(returnData);
}
return returnData;
}
catch (Exception ex)
{
LogErrorMessages("ExecuteStoredProcedure", ex.Message);
throw;
}
The SqlDataAdapter will open and close the connection itself - no need to do that explicitly.
Furthermore, I would
define a sensible max length for your NVARCHAR parameters (what does the stored proc except)
pass the dates as DATETIME ! (not NVARCHAR)
pass the boolean value as BIT ! (not NVARCHAR)
when you re-throw an exception, only use throw and not throw ex (if you use throw ex, you're basically breaking the stack trace and cannot figure out where the exception really came from)
I had a similar issue with stored procedures running slower than the same query in the query window.
I had tried everything from coding for parameter sniffing (local variables), removing clustered indexes and using only non-clustered, etc. It still took 22 seconds minuimum to retrieve a varchar field, using a nvarchar parameter.
I originally thought it was just the difference between nvarchar and varchar and changed the database field to nvarchar. I lost another full day, moving 50 million records to a new table and re-indexing. Still took over 22 seconds.
Finally, I changed any key fields in the tables from nvarchar to varchar, plus all the parameters and wow; back dowen to less than 1 second.
I strongly believe this is a bug in SQL Server, that has never got corrected. How can you run a query directly in the query window, or call sql from vb.net or c# code and get results in less than 1 second; then run the same query, using parameters, in a stored procedure and get such horrendus results?
Short answer: Stay away from nvarchar data types at all costs.
Also, learn to use the merge statement to move data into large tables to avoid timeouts.
Set recovery mode to simple, while running a large merge query; then put back to full recovery.
Boy, did I learn alot this week. Over 80 hours of education that I did not need.
Are you using any transactions in your stored procedure? Uncommitted transactions will cause this exact error message.
Make sure the paramters being passed to SP match the ones on the databse [thi is Case sensitive ]
I had same issue. My stored proc executed from MSSMS or dbForgeStudio, but not from C# code (SqlCommand). I fixed this problem by altering stored proc in SQL server (without any changes).
I tried with "with recompile", with "arithabort off", to change the code etc etc but at the end only restarting sql server I solved the problem.
We had this same issue and thought it was parameter sniffing also, but after trying many things including changing parameter sniffing, using with recompile, drop/recreate, updating statistics, freeing proc cache, we found none of these worked. We tracked it down to a single index that needed to be added. We had just switched from the legacy cardinality estimator to the post-2014 estimator. Switching back to the legacy estimator resolved the issue, or using the new estimator and adding the index.
change the CommandTimeout=0
sqlCmd.CommandTimeout = 0;