How can I get the number of records affected by a stored procedure? - sql

For INSERT, UPDATE and DELETE SQL statements executed directly against the database, most database providers return the count of rows affected. For stored procedures, the number of records affected is always -1.
How do we get the number of records affected by a stored procedure?

Register an out parameter for the stored procedure, and set the value based on ##ROWCOUNT if using SQL Server. Use SQL%ROWCOUNT if you are using Oracle.
Mind that if you have multiple INSERT/UPDATE/DELETE, you'll need a variable to store the result from ##ROWCOUNT for each operation.

##RowCount will give you the number of records affected by a SQL Statement.
The ##RowCount works only if you issue it immediately afterwards. So if you are trapping errors, you have to do it on the same line. If you split it up, you will miss out on whichever one you put second.
SELECT #NumRowsChanged = ##ROWCOUNT, #ErrorCode = ##ERROR
If you have multiple statements, you will have to capture the number of rows affected for each one and add them up.
SELECT #NumRowsChanged = #NumRowsChanged + ##ROWCOUNT, #ErrorCode = ##ERROR

Turns out for me that SET NOCOUNT ON was set in the stored procedure script (by default on SQL Server Management Studio) and SqlCommand.ExecuteNonQuery(); always returned -1.
I just set it off: SET NOCOUNT OFF without needing to use ##ROWCOUNT.
More details found here : SqlCommand.ExecuteNonQuery Method#Remarks
When SET NOCOUNT ON is set on the connection (before or as part of executing the command, or as part of a trigger initiated by the execution of the command) the rows affected by individual statements stop contributing to the count of rows affected that is returned by this method.
If no statements are detected that contribute to the count, the return value is -1. If a rollback occurs, the return value is also -1.

For Microsoft SQL Server you can return the ##ROWCOUNT variable to return the number of rows affected by the last statement in the stored procedure.

##ROWCOUNT

WARNING: ##ROWCOUNT may return bogus data if the table being altered has triggers attached to it!
The ##ROWCOUNT will return the number of records affected by the TRIGGER, not the actual statement!

Related

Do I have to include "SELECT ##RowCount" if I have more than one SQL statement?

I know that, if I execute a single SQL statement that UPDATEs or DELETEs some data, that it will return the number of rows affected.
But if I have multiple SQL statements in a sql script, and I want to know the number of rows affected from the last statement executed, will it still return that automatically, or do I need a
SELECT ##RowCount
at the end of the script?
The code in question is not a Stored Procedure. Rather, it is a parameterized SQL script stored in an arbitrary location, executed using the ExecuteStoreCommand function in Entity Framework, as in:
var numberOfRowsAffected = context.ExecuteStoreCommand<int>(mySqlScript, parameters);
It depends on the NOCOUNT setting when executing your quer(y/ies).
If NOCOUNT is ON then no DONE_IN_PROC messages will NOT be returned.
If NOCOUNT is OFF, the default setting, then DONE_IN_PROC messages will be returned, (eg. counts).
Both of these situations are different to executing,
SELECT ##ROWCOUNT;
which will return a result set with a single scalar value, different from a DONE_IN_PROC message. This will occur, regardless of the setting of NOCOUNT.
I believe that SELECT ##ROWCOUNT is sometimes used to make Entity Framework "play" with more complex TSQL statements because EF both requires
Requires a count for post validation
And will accept a scalar number result set as a substitute for a DONE_IN_PROC message.
Its important that SELECT ##ROWCOUNT; is executed immediately after the last query statement because many statements will reset ##ROWCOUNT and therefore yield an unexpected result.
Just to be specific on answer part, you would need to add SELECT ##RowCount to return number of rows affected by last statement.
I think confusion might be due to rows returned in SSMS window while executing query.By default SSMS shows number of rows returned for all sql statements but it returns affected rows as message not a dataset.
##ROWCOUNT will automatically return number of rows effected by the last statement.
Please find the msdn link here
https://msdn.microsoft.com/en-us/library/ms187316.aspx

Initial value of ##ROWCOUNT varies across databases and servers

We are attempting to run the a DELETE statement inside a WHILE loop (to avoid large transaction logs for lots of rows) as follows:
WHILE (##ROWCOUNT > 0)
BEGIN
DELETE TOP (250000)
FROM
MYDATABASE.MYSCHEMA.MYTABLE
WHERE
MYDATABASE.MYSCHEMA.MYTABLE.DATE_KEY = 20160301
END
When this command is executed inside a new SQL Server Management Studio connection in our development environment, it deletes rows in blocks of 250K, which is the expected behavior.
When this command is executed in the same way on our test server, we get the message
Command completed successfully
That is, the WHILE loop was not entered when the statement was run.
After some additional investigation, we have found that the behavior also varies depending on the database that we connect to. So if the code is run (in our test environment) while SQL Server Management Studio is connected to MYDATABASE, the DELETE statement does not run. If we run the code while connected to SOME_OTHER_DATABASE, it does.
We partially suspect that the value of ##ROWCOUNT is not reliable, and may be different for different connections. But when we run the code multiple times for each database & server combination, we see behavior that is 100% consistent. So random initial values of ##ROWCOUNT do not appear to explain things.
Any suggestions as to what could be going on here? Thanks for your help!
Edit #1
For those asking about the initial value of ##ROWCOUNT and where it is coming from, we're not sure. But in some cases ##ROWCOUNT is definitely being initialized to some value above zero, as the code works on a fresh connection as-is.
Edit #2
For those proposing the declaration of our own variable, for our particular application we are executing SQL commands via a programming language wrapper which only allows for the execution of one statement at a time (i.e., one semicolon).
We have previously tried to establish the value of ##ROWCOUNT by executing one delete statement prior to the loop:
Statement #1:
DELETE TOP (250000)
FROM
MYDATABASE.MYSCHEMA.MYTABLE
WHERE
MYDATABASE.MYSCHEMA.MYTABLE.DATE_KEY = 20160301
Statement #2 (##ROWCOUNT is presumably now 250,000):
WHILE (##ROWCOUNT > 0)
BEGIN
DELETE TOP (250000)
FROM
MYDATABASE.MYSCHEMA.MYTABLE
WHERE
MYDATABASE.MYSCHEMA.MYTABLE.DATE_KEY = 20160301
END
However, whatever is causing ##ROWCOUNT to take on a different value on start-up is also affecting the value between commands. So in some cases the second statement never executes.
You should not use a variable before you have set its value. That is equally true for system variables.
The code that you have is very dangerous. Someone could add something like SELECT 'Here I am in the loop' after the delete and it will break.
A better approach? Use your own variable:
DELCARE #RC int;
WHILE (#RC > 0 OR #RC IS NULL)
BEGIN
DELETE TOP (250000)
FROM MYDATABASE.MYSCHEMA.MYTABLE
WHERE MYDATABASE.MYSCHEMA.MYTABLE.DATE_KEY = 20160301;
SET #RC = ##ROWCOUNT;
END;
Where are you getting your initial ##ROWCOUNT from? I mean, you're never going to enter that block, because ##ROWCOUNT would be expected to be zero, so you'd never enter the loop. Also, deleting in 250K batches wouldn't change the size of your transaction log - all of the deletions will be logged if you're logging, so there's no benefit (and some penalty) for doing this w/in a loop.
Have you traced the session? Since ##ROWCOUNT returns the number of rows affected by the prior statement in the session, I would guess that either the last query SSMS executes as part of establishing the session returns a different number of rows in the two environments or that you have a login trigger in one or the other environments whose last statement returns a different number of rows. Either way, a trace should tell you exactly why the behavior is different.
Fundamentally, though, it makes no sense to refer to ##ROWCOUNT before you run the statement that you are interested in getting a count for. It's easy enough to fix this using a variable
DECLARE cnt integer = -1;
WHILE (cnt != 0)
BEGIN
DELETE TOP (250000)
FROM MYDATABASE.MYSCHEMA.MYTABLE
WHERE MYDATABASE.MYSCHEMA.MYTABLE.DATE_KEY = 20160301;
SET cnt = ##ROWCOUNT;
END

SQL Server - is using ##ROWCOUNT safe in multithreaded applications?

I am using SQL Server 2008.
I have a table A which accepts many insert/update in one seconds. After insert, update I want to get the number of rows affected.
INSERT INTO A (ID) VALUES (1)
IF ##ROWCOUNT = 0
PRINT 'NO ROWS AFFECTED'
While query is being executed, the same query may be called again by application. So what happens if the current execution is after INSERT but before IF block at that moment.
Do you think ##ROWCOUNT may give wrong result for that reason?
Or is it always safe in its context?
Yes - its safe. It always refers the previous operation in current query
BUT
if you want to know the number of rows affected, save it to variable first, because after IF statement the count ##ROWCOUNT resets
INSERT INTO A (ID) VALUES (1)
DECLARE #rc INT = ##ROWCOUNT
IF #rc = 0
PRINT 'NO ROWS AFFECTED'
ELSE
SELECT #rc AS RowsAffected
##ROWCOUNT is both scope and connection safe.
In fact, it reads only the last statement row count for that connection and scope. The full rules are here on MSDN (cursors, DML, EXECUTE etc)
To use it in subsequent statements, you need to store it in a local variable.
You must preserve the ##ROWCOUNT value in a local variable, otherwise after the IF statement its value will reset to zero:
SET #rowCount = ##ROWCOUNT
IF #rowCount = 0
PRINT 'NO ROWS AFFECTED'
Other than that, yes, it is safe.
Short answer: Yes.
However it worth to see the question in a perspective, for the deeper understanding why the answer yes is so natural without doubt.
SQL Server is prepared to handle concurrent access correctly by its nature, regardless if the client application is multithreaded or not. Unless this attribute SQL Server would be useless in any multiuser scenario. From point of view of the server it does not matter if the concurrent access caused by one multithreaded application, or two applications which are currently using the server concurrently by multiple users.
Regarding this point the ##rowcount is only the top of the iceberg, there are much more and deeper functionality what must be handled correctly when concurrent access is in the picture.
The most practical part of this area is transaction management and transaction isolation.

SET NOCOUNT OFF or RETURN ##ROWCOUNT?

I am creating a stored procedure in Sql Server 2008 database. I want to return the number of rows affected. Which is a better option SET NOCOUNT OFF or RETURN ##ROWCOUNT?
ALTER PROCEDURE [dbo].[MembersActivateAccount]
#MemberId uniqueidentifier
AS
BEGIN
-- Should I use this?
SET NOCOUNT OFF;
UPDATE [dbo].Members SET accountActive = 1 WHERE id = #MemberId;
--Or should I SET NOCOUNT ON and use the following line instead?
--return ##ROWCOUNT;
END
I know that both work, but which is a better choice and why?
After some trying I am coming to a conclusion that SET NOCOUNT is OFF by default inside stored procedures. Is it possible to change this behavior inside my database?
Use ##RowCount. It's explicit and transparent, it is entirely controlled by your code rather than a built-in behaviour.
The NOCOUNT option can be manually set to default to ON (Optons>Query Execution>SQL Server>Advanced). If you set it this way but then declare SET NOCOUNT OFF in your stored procedure then that local setting takes precedence.
Don't use RETURN for values. By convention RETURN from stored procedures is for error codes, 0 meaning no error and non-0 meaning some kind of problem. If you need data back, the appropriate way to do it is with an OUTPUT parameter. It's a little counter-intuitive based on other languages' use of return.
I know that having SET NOCOUNT ON would make a DataAdapter think there was a concurrency conflict.
You can read about it on MSDN. If the code is going to be used by DataAdapters then obviously don't use SET NOCOUNT ON.
It looks like SqlCommand also has this behaviour, which I guess is the reason why the DataAdapter has a problem (as under the hood it will use a Command object).
Reasons for using SET NOCOUNT ON/OFF:
To control the stack overflow while inserting rows into any table.
Passing the T-Sql messages while executing of the queries or nested queries.
To Show or viewing the latest queries executed.
To get information on the latest record escalation.
Why we use SET NOCOUNT on/off ---
Ans : we can understand this by following steps
step 1 : execute query "Select top 10 * from table name".
step 2 : open message window it shows a message "10 rows affected". it creates extra overheads and extends our execution time.
step 3 : to overcome this extra overheads we use SET NOCOUNT ON. If it is On then it will never count the number of row returns instead it sows a message commands completed successfully.
step 4 : By default NOCOUNT is ON then it counts the number of returned rows that is why my suggestion that it should off during creating new procedures to get better performance from database server.

Is this stored procedure thread-safe? (or whatever the equiv is on SQL Server)

With the help of others on SO I've knocked up a couple of Tables and Stored Procedures, this morning, as I'm far from a DB programmer.
Would someone mind casting an eye over this and telling me if it's thread-safe? I guess that's probably not the term DBAs/DB developers use but I hope you get the idea: basically, what happens if this sp is executing and another comes along at the same time? Could one interfere with the other? Is this even an issue in SQL/SPs?
CREATE PROCEDURE [dbo].[usp_NewTicketNumber]
#ticketNumber int OUTPUT
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO [TEST_Db42].[dbo].[TicketNumber]
([CreatedDateTime], [CreatedBy])
VALUES
(GETDATE(), SUSER_SNAME())
SELECT #ticketNumber = IDENT_CURRENT('[dbo].[TicketNumber]');
RETURN 0;
END
You probably do not want to be using IDENT_CURRENT - this returns the latest identity generated on the table in question, in any session and any scope. If someone else does an insert at the wrong time you will get their id instead!
If you want to get the identity generated by the insert that you just performed then it is best to use the OUTPUT clause to retrieve it. It used to be usual to use the SCOPE_IDENTITY() for this but there are problems with that under parallel execution plans.
The main SQL equivalent of thread safety is when multiple statements are executed that cause unexpected or undesirable behaviour. The two main types of such behaviour I can think of are locking (in particular deadlocks) and concurrency issues.
Locking problems occur when a statement stops other statements from accessing the rows it is working with. This can affect performance and in the worst scenario two statements make changes that cannot be reconciled and a deadlock occurs, causing one statement to be terminated.
However, a simple insert like the one you have should not cause locks unless something else is involved (like database transactions).
Concurrency issues (describing them very poorly) are caused by one set of changes to database records overwriting other changes to the same records. Again, this should not be a problem when inserting a record.
The safest way to go here would probably be to use the Output clause, since there is a known bug in scope_idendity under certain circumstances ( multi/parallel processing ).
CREATE PROCEDURE [dbo].[usp_NewTicketNumber]
AS
BEGIN
DECLARE #NewID INT
BEGIN TRANSACTION
BEGIN TRY
declare #ttIdTable TABLE (ID INT)
INSERT INTO
[dbo].[TicketNumber]([CreatedDateTime], [CreatedBy])
output inserted.id into #ttIdTable(ID)
VALUES
(GETDATE(), SUSER_SNAME())
SET #NewID = (SELECT id FROM #ttIdTable)
COMMIT TRANSACTION
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
SET #NewID = -1
END CATCH
RETURN #NewID
END
This way you should be thread safe, since the output clause uses the data that the insert actually inserts, and you won't have problems across scopes or sessions.
CREATE PROCEDURE [dbo].[usp_NewTicketNumber]
#NewID int OUTPUT
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRY
BEGIN TRANSACTION
INSERT INTO
[dbo].[TicketNumber] ([CreatedDateTime], [CreatedBy])
VALUES
(GETDATE(), SUSER_SNAME())
SET #NewID = SCOPE_IDENTITY()
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF XACT_STATE() <> 0
ROLLBACK TRANSACTION;
SET #NewID = NULL;
END CATCH
END
I would not use RETURN for meaningful use data: either recordset or output parameter. RETURN would normally be used for error states (like system stored procs do in most cases):
EXEC #rtn = EXEC dbo.uspFoo
IF #rtn <> 0
--do error stuff
You can also use the OUTPUT clause to return a recordset instead.
This is "thread safe", that is it can be run concurrently.
First off - why don't you just return the new ticket number instead of 0 all the time? Any particular reason for that?
Secondly, to be absolutely sure, you should wrap your INSERT and SELECT statement into a TRANSACTION so that nothing from the outside can intervene.
Thirdly, with SQL Server 2005 and up, I'd wrap my statements into a TRY....CATCH block and roll back the transaction if it fails.
Next, I would try to avoid specifying the database server (TestDB42) in my procedures whenever possible - what if you want to deploy that proc to a new server (TestDB43) ??
And lastly, I'd never use a SET NOCOUNT in a stored procedure - it can cause the caller to erroneously think the stored proc failed (see my comment to gbn below - this is a potential problem if you're using ADO.NET SqlDataAdapter objects only; see the MSDN docs on how to modify ADO.NET data with SqlDataAdapter for more explanations).
So my suggestion for your stored proc would be:
CREATE PROCEDURE [dbo].[usp_NewTicketNumber]
AS
BEGIN
DECLARE #NewID INT
BEGIN TRANSACTION
BEGIN TRY
INSERT INTO
[dbo].[TicketNumber]([CreatedDateTime], [CreatedBy])
VALUES
(GETDATE(), SUSER_SNAME())
SET #NewID = SCOPE_IDENTITY()
COMMIT TRANSACTION
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
SET #NewID = -1
END CATCH
RETURN #NewID
END
Marc
I agree with David Hall's answer, I just want to expand a bit on why ident_current is absolutely the wrong thing to use in this situation.
We had a developer here who used it. The insert from the client application happened at the same time the database was importing millions of records through an automated import. The id returned to him was from one of the records my process imported. He used this id to create records for some child tables which were now attached to the wrong record. Worse we now have no idea how many times this happened before someone couldn't find the information that should have been in the child tables (his change had been on prod for several months). Not only could my automated import have interfered with his code, but another user inserting a record at the smae time could have done the same thing. Ident_current should never be used to return the identity of a record just inserted as it is not limited to the process that calls it.