Output parameters not updated after ExecuteReader() - c#

When I run cmd.ExecuteScalar() or cmd.ExecuteNonQuery() the Output and InputOutput parameters on the command object get updated from the changes in the stored procedure. However the same does not happen for cmd.ExecuteReader(). This is happening on both Sql Server and MySql Connections. Is this behavior by design?

Hey this may help you. clicky...
It appears this can possibly be an issue under certain circumstances.

The output parameters are only available after you read to the end of the recordset.
For example, in this procedure:
alter procedure db.TestProc(#p int output)
as
select 1
select 1
set #par = 1
The database will only set #par after you've read both recordsets. The database doesn't even execute the second SELECT before you're done reading the first. It is streaming results as you request them.

You should be able to retrieve the value via an Output parameter. Take a look at this MS Support article and see if your issue is one of those mentioned: link text
Also, what are you trying to return? If it's just a single value, it would be worth using ExecuteScalar() rather than ExecuteReader().

Related

Entity Framework - extremely long execution time for stored procedure

I'm following up on my question yesterday, Entity Framework 6 get complext return value from a stored procedure. My stored procedure now runs under entity framework. However, it times out after 3 minutes, the connection time out.
I run the stored procedure in my SQL Server Management Studio with the line (customer information omitted):
EXEC spGetDupWOs #ProjectName=N'...', #City=N'...', #State=N'LA', #ProposalNum=N'201703080740-001', #County=N'...', #Owner=N'...', #QuoteRecipients=N'...', #ProjectID=-1
It executes in less than a second. When Entity framwork executes it, it takes forever.
Using the SQL Server Profiler, I determined that Entity Framework is sending this line to the SQL server:
exec sp_executesql N'EXEC spGetDupWOs',N'#ProjectName nvarchar(19),#City nvarchar(6),#State nvarchar(2),#ProjectNum nvarchar(12),#County nvarchar(10),#Owner nvarchar(23),#QuoteRecipients nvarchar(23),#ProjectID bigint',#ProjectName=N'...',#City=N'Holden',#State=N'LA',#ProposalNum=N'201703080740-001',#County=N'Livingston',#Owner=N'...',#BID_RECIP=N'...',#ProjectID=-1
When I run this in SSMS, it takes forever to run.
Reading the similar questions it looks like the issue is Parameter Sniffing and a change in execution plan.
Here is my call to execute the stored procedure in my application:
List<DuplicateProposals> duplicateCheckResults =
db.Database.SqlQuery<DuplicateProposals>("spGetDupWOs",
spl.ToArray())
.ToList();
After reading a bunch of articles online, I'm even more confused. How can I change my call to resolve this?
The issue identified is parameter sniffing in SQL Server. There are multiple approaches to handle this, but the most optimal for your scenario depends on your real use case, utilization, etc.
Here are some options.
Recompile the stored procedure with every execution. This may become very heavy CPU utilization, and is typically overkill. I would NOT recommend this option unless you have a very good reason. To implement: Use
WITH RECOMPILE or the OPTION(RECOMPILE) hint at the end of your query.
Optimize for hint. This can be a workaround for the parameter sniffing, but may result in a subpar execution plan for all of your queries. Typically, not an optimal approach. To implement: Use OPTION(OPTIMIZE FOR UNKNOWN)
Copy parameter to a local variable. Was more common in older versions of SQL Server. To implement: Declare a local variable, then copy the value from your input parameter to your local variable. DECLARE #local_var1 char(1) = #InputParam1;
Turn off parameter sniffing at query level. This approach uses the QUERYTRACEON hint. This may be the most optimal approach for this case scenario. I would recommend to explore this option as a primary strategy. To implement: add OPTION(QUERYTRACEON 4136) to the end of your query.
Example:
SELECT * FROM dbo.MyTable T
WHERE T.Col1 = #Param1 and T.Col2 = #Param2
OPTION(QUERYTRACEON 4136)
I ended up having to convert the entire call into a single string that I passed to the SqlQuery function.
string sql = string.Format("exec spGetDupWOs #ProjectName=N'{0}',#City=N'{1}',#State=N'{2}',#ProjectNumber=N'{3}',#County=N'{4}',#Owner=N'{5}',#QuoteRecipients=N'{6}',#ProjectID={7}",
project.ProjectName,
project.City,
project.State,
project.ProjectNumber,
project.County,
project.Owner,
quoteRecipientsList,
"null");
Yes, I had to include the N prefix to the strings to make it work, I'm not sure why but it worked.
Thanks for all of the help everyone. I could not have solved this without your help.

How to write thread safe SQL Server stored procedures

I have some stored procedures in which multiple queries are being executed. To get last identity of insert I am using IDENT_CURRENT which is causuing problem.
My question is can I have lock statements like C# in T-SQL so that it can be thread safe?
EDIT: Code I am using
INSERT INTO activities
(creator
,title
,description)
VALUES
(#Creator
,#Tile
,#Description)
SET #ActivityId = IDENT_CURRENT('dbo.activities');
INSERT INTO [dbo].activity_cfs
([activity_id],
[cf_id],
[cf_field_name],
[field_key],
[value])
SELECT
#ActivityId,
cf_id,
cf_field_name,
field_key,
value
FROM #ActivityCustomFields
#ActivityCustomFields is my temp table.
It is quite likely that you should use SCOPE_IDENTITY instead of IDENT_CURRENT. There are many explanations how they differ, for example: What is the difference between Scope_Identity(), Identity(), ##Identity, and Ident_Current?
But, if you really need to guarantee that certain part of the code is not being run by several threads at the same time, you can use sp_getapplock.
Based on the code that you added to the question I'm now pretty sure that you should simply use SCOPE_IDENTITY, like this:
INSERT INTO activities
(creator
,title
,description)
VALUES
(#Creator
,#Tile
,#Description);
SET #ActivityId = SCOPE_IDENTITY();
INSERT INTO [dbo].activity_cfs
([activity_id],
[cf_id],
[cf_field_name],
[field_key],
[value])
SELECT
#ActivityId,
cf_id,
cf_field_name,
field_key,
value
FROM #ActivityCustomFields;
The SCOPE_IDENTITY function returns the last identity created in the same session and the same scope.
The IDENT_CURRENT returns the last identity created for a specific table or view in any session. So, if you have several threads that run this procedure simultaneously IDENT_CURRENT will see identities generated in other threads, which you don't want.
SCOPE_IDENTITY would be the way to go in my understanding. Please follow following link that in SQL Server when using SCOPE_IDENTITY will always be threadsafe:
http://www.vbforums.com/showthread.php?727047-RESOLVED-Is-MSSQL-s-SCOPE_IDENTITY()-thread-safe

Oracle stored Procedure to return multiple resultsets to .net

I have written the following stored procedure to return multiple data sets to my front end .Net application
create or replace PROCEDURE GET_EMPLOYEE_INFO
(
EMP_NO IN VARCHAR2,
E_RECORD_SET1 OUT SYS_REFCURSOR,
E_RECORD_SET2 OUT SYS_REFCURSOR,
E_RECORD_SET3 OUT SYS_REFCURSOR,
E_RECORD_SET4 OUT SYS_REFCURSOR
) AS
BEGIN
OPEN E_RECORD_SET1 FOR
SELECT * FROM EMP.EMPLOYEES;
OPEN E_RECORD_SET2 FOR
SELECT * FROM EMP.CITIES;
OPEN E_RECORD_SET3 FOR
SELECT * FROM EMP.STATES;
OPEN E_RECORD_SET1 FOR
SELECT * FROM EMP.DURATION;
Each query executes correctly on its own. When I execute the stored procedure, it displays the result sets but I don't believe that all the data is being fetched. At the bottom, it still says "RUNNING IDE CONNECTION".
Is the procedure looping?
Is there a limitation regarding the SYS_REFCURSOR type?
Do I need to close these cursors?
I assume that your actual stored procedure is not missing the END at the end. The code you posted won't compile.
I assume that your stored procedure is also doing something with the EMP_NO parameter.
And I assume that despite the name implying that it is a number, EMP_NO is actually declared as a string.
Assuming all that is true
Why do you believe that all the data is not being fetched?
What is the "it" that the sentence "At the bottom, it still says" refers to? Is this your C# application? Your IDE? Something else?
The code you posted, assuming the END is added so that it compiles, cannot loop. Your code simply opens the cursors, it does not execute the SQL statements and it does not generate any data. That won't happen until the client application starts fetching data.
Although it is an unusual design, it is perfectly syntactically valid to have a procedure that returns four SYS_REFCURSOR parameters. That won't affect the data that is fetched from the cursors.
Yes, your client application needs to close these cursors once it has fetched all the data from them.

Returning a string in SQL from a stored procedure

Is it possbible to return a string in SQL?
I have a stored procedure, and this creates a nice text string. Would I have to pass this into a temp table and somehow read it from C# or can I simply return a string
just add that at the end of you stored procedure:
select myString
If the stored procedure only returns a single row, just call ExecuteScalar, which returns the first row and column of the query you executed. Here's an MSDN article on the subject.
I would suggest that you use a FUNCTION instead of a STORED PROCEDURE. You can return either tables or whatever you want from a function.
There are some limitations though. You can dig a little deeper into the differences to see what is used when. Here's a link that can help you out get started:
Function vs. Stored Procedure in SQL Server
If you want to use stored procedure anyway, you can either return a single row, single column result set, using SELECT, or just use an output parameter.
select "ok"
or
return "ok"
you can try
Fixed by using XML PATH and select as text.
I would ask WHY are you returning a single string from SQL Server? Occasionally it might be appropriate, but SQL Server is designed to return sets of data. If you are looking at individual values, you might want to rethink your architecture.
Bring back the whole row. Decide to filter it to a single column on the business side (in .NET). Use the DataReader if you are concerned about optimizing.

SET NOCOUNT ON and reading messages using C# and ADO.NET

SET NOCOUNT ON stops the message that shows the count of the number of rows affected by a Transact-SQL statement or stored procedure from being returned as part of the result set.
a) How can you read these messages using C# and ADO.NET ( I assume C# code reading these messages is the same regardless of whether T-SQL statements were executed in a stored procedure, batch or… )?
b) Assuming stored procedure contains several statements, how can your C# code identify to what SQL statement does particular messages refer to?
Thank you
Informational messages (like the rows affected count info) are reported in ADO.Net through the SqlConnection.InfoMessage event. Add a delegate to the event and will be invoked whenever the server transmits an informational message (ie. any error message with severity bellow 10).
there is no way to associate informational messages like afffected count info with the source. You're going to have to do it based on knowledge of the logic and understand that the first message refers to the first update, the second message to the second update etc.
Relying on affected rows count in the client is generaly a bad practice. The many issues ORM layers like NHibernate and ADO.Net datasets have when SET NOCOUNT ON is turned on just shows how problematic this practice is.
Don't rely on it. Best practice is SET NOCOUNT ON (discussed with my question here)
When you load your datatable, use .Count.
Use an OUTPUT parameter to pass ##ROWCOUNT back (or as a dataset)
Take a look at this question and answers. You can't do (b) above without adding some code in your TSQL that captures the ##rowcount and outputs it in some manner (like a resultset that you could read from).
One option is in your stored procedure is to include variables that you will pass back statement counts. You can do by creating your procedure with the needed OUTPUT parameters.
FIRST SQL HERE
#FirstSQLCount = ##ROWCOUNT
SECOND SQL HERE
#SecondSQLCount = ##ROWCOUNT

Categories

Resources