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

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

Related

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

nvarchar(max) slowing stored procedure

From yesterday, i'm facing a problem:when i call a stored proc from c#,it lasts >5 in, but when i execute it directly from SSMS (in the server machine) its lasts less than 30 seconds.
I have searched in forums and went trough this great article http://www.sommarskog.se/query-plan-mysteries.html but no result.
The script contained in my proc is retrieving 10 columns among them a column called "article" of type nvarchar(max).
When i remove the article column from my Select ,my proc executes quickly.
To further my logic, i have created a new stored proc retrieving just Primary Key Column and nvarchar(max) column.
I'm reproducing the same behaviour.Here is my new proc=MyNewProc(lasts >5 min when called from c# and 0 Secondes in the server from SSMS)
CREATE PROCEDURE Student.GetStudents
AS
BEGIN
SET NOCOUNT ON
-----------------
SELECT StudentId,Article
FROM Students
WHERE Degree=1
END
MyNewProc returns just 2500 rows.
Is that normal? How can i improve that.
SELECT SUM(DATALENGTH(Article)) FROM Students WHERE Degree=1
the result is 13885838
You're probably transferring a lot of data over the network. That takes time.
Instead of returning article try returning LEFT(article, 50) to see if its an issue with the volume of data or not.
One thing to note is that SSMS will begin populating the results immediately while a C# application probably will not.
In SSMS, go to the following: Tools -> Options
Then go to Query Execution -> SQL Server -> Advanced
From here, look at what check boxes are checked and if there is something that is checked, SSMS will use this automatically when you execute a sproc from inside of it but when you execute it from C# (or whatever client you're using) it won't be used.
I had this same issue and found out that I needed to include the following line at the top of my sproc and it worked perfectly:
SET ARITHABORT ON;

Show how many rows were deleted

I use C# program and my database is in SQL server 2008.
When user deleted some rows from database, I want to show him/her in windows application how many rows deleted.
I want to know how I can send SQL message to C# and show it for user.
For example when I deleted 4 rows from table, SQL show message like (4 row(s) affected). Now I want to send number 4 to my C# program. How can I do it? Thank you.
If you are using SqlCommand from your .NET application to perform your delete/update, the result of ExecuteNonQuery() returns the number of rows affected by the last statement of the command.
See http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.executenonquery.aspx.
If you're using the System.Data.SqlClient.SqlCommand.ExecuteNonQuery method or System.Data.Common.DbCommand.ExecuteNonQuery method, then the return value should be the number of rows affected by your statement (the last statement in your command, I think).
There is a caveat to this...if you execute a batch or stored procedure that does SET NOCOUNT ON, then the number of rows affected by each statement is not reported and ExecuteNonQuery will return -1 instead.
in T-SQL, there is a ##rowcount variable that you can access in order to get the number of rows affected by the last statement. Obviously you would need to grab that immediately after your DELETE statement, but I believe you could do a return ##rowcount within your T-SQL if you are using SET NOCOUNT ON.
Alternatives would be to return the value as an OUTPUT parameter, especially if you have a batch of multiple statements and you'd like to know how many rows are affected by each. Some people like to use the T-SQL RETURN statement to report success/failure, so you may want to avoid returning "number of rows affected" for consistency's sake.
I imagine you would want to do a select "count" on the delete statement before you issue the delete, then capture the number and manipulate it as needed.
Use the ##RowCount SQL Environment variable.
You can return it from a Stored Procedure if you are using them.

How is a stored procedure processed by Sql Server and .Net

I have been using a stored procedure for more than 1.5 years. But I've never considered how data is retrieved from the UI or within another stored procedure.
When I write a simple stored procedure.
eg.
CREATE PROCEDURE sp_test
AS
BEGIN
SELECT * FROM tblTest --Considering table has 3 columns.
END
How does C# gets this result into DataTable.
Whenever I have to use the result of this procedure in another procedure, I think we have to create a table valued parameter using the table datatype and assign its result to a table variable. I've never tried it.
CREATE PROCEDURE sp_testcall
AS
BEGIN
#temp = exec sp_test -- I think this would be the way, never tried
END
If the above sample code is true, then what is the difference between using the above method and a query to insert records into a temporary table?
CREATE PROCEDURE sp_test
AS
BEGIN
SELECT * INTO #tmp FROM tblTest --Considering table has 3 columns.
END
It would seem that copying the result into a temporary table requires another effort by sql server.
But what would be going on behind the scenes? Would it directly assign references of the result into a table valued parameter or does it use the same process as a temporary table?
My question might not be clear. But I will try to improve.
For an beginer to intermediate level you should always consider #temp tables and #table variables two faces of the same coin. While there are some differences between them, for all practical purposes they cost the same and behave nearly identical. The sole major difference is that #table variables are not transacted and hence not affected by rollbacks.
If you drill down into details, #temp tables are slightly more expensive to process (since they are transacted) but on the other hand #table variables have only the lifetime of a variable scope.
As to other issues raised by your question:
table value parameters are always read only and you cannot modify them (insert/update/delete into them)
tacking the result set of a procedure into a table (real table, #temp table or #tabel variable, doesn't matter) can only be done by using INSERT INTO <table> EXEC sp_test
as a rule of thumb a procedure that produces a result that is needed in another procedure is likely to be better of as a User Defined Function
The topic of sharing data between procedures was analyzed at length by Erland Sommarskog, see How to Share Data Between Stored Procedures.
A select means "return data to client". C# is a client, therefore it gets the data.
Then again, it's not exactly C# that does it, it's ADO.NET. There's a data provider that knows how to use a network/memory/some other protocol to talk to the SQL server and read data streams it generates. This particular client (ADO.NET) uses the received data to construct certain classes, such as DataTable, other providers can do something completely different.
All that is irrelevant at SQL Server level, because as far as the server is concerned, the data has been sent out using the protocol with which the connection was established, that's it.
From inside, it doesn't make much sense to have a stored procedure return simply selected data to anything else.
When you need to do that, you have the means to explicitly tell SQL Server what you want, such as inserting the data into a temporary table available to both involved SPs, inserting data into a table-valued parameter passed to the procedure, or rewriting your stored procedure as a function that returns a table.
Then again, it's not exacly clear to me what you were asking about.

Output parameters not updated after ExecuteReader()

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().

Categories

Resources