value of the variable ##rowcount - sql

having the following sql script:
declare #table table(id int)
insert into #table values (1),(2)
--print ##rowcount
if 3 < 2
print 'false'
--print ##rowcount
if I'm uncommenting the first print, it will print he value 2, but if I am uncommenting the last print, it will print 0. So the instruction IF is affecting the value of the variable ##ROWCOUNT? Or what is the scope of this variable?
I am using sql server 2014.

The Global variable ##ROWCOUNT will return the number of rows affected by the last statement. Run after the INSERT statement, it will return 2 (rows). Run after the IF statement, it will return the number of rows affected by the IF statement, which is zero.
This also means in the SQL below the 2 ##rowcount statements return 2 then 0 (zero) as the first ##rowcount statement affects zero rows
declare #table table(id int)
insert into #table values (1),(2)
print ##rowcount
print ##rowcount

Yes. ##ROWCOUNT refers to the previous statement and it is constantly being reassigned. This is why it is usually used for assignment to a variable:
declare #rowcnt int;
<whatever>
set #rowcnt = ##ROWCOUNT;

The documentation for ##ROWCOUNT gives examples of how it works in some situations, but doesn't list IF specifically.
However, with a bit of testing, it appears that an IF statement that was not executed resets it to 0. This makes sense if you think of IF as a statement that wraps the statement/block inside it:
if the condition was true, the IF statement affected however many rows the statement inside affected
if the condition was not true, and there is an ELSE block, it affected however many rows the statement inside that affected
if the condition was not true, and there is no ELSE it affects zero rows
Compare:
DECLARE #TEST TABLE(id INT)
INSERT INTO #TEST VALUES (1),(2),(3)
IF 1 = 2
SELECT * FROM #TEST
PRINT ##ROWCOUNT
-- 0, because block didn't run
IF 1 = 1
SELECT * FROM #TEST
PRINT ##ROWCOUNT
-- 3, i.e. number of rows in SELECT
IF 1 = 2
SELECT TOP 1 * FROM #TEST
ELSE
SELECT TOP 2 * FROM #TEST
PRINT ##ROWCOUNT
-- 2, i.e. number of rows in the SELECT executed in the ELSE clause

Related

Why does ##ROWCOUNT return 1 for a NULL statement using sp_executesql?

If I run this:
DECLARE #sql NVARCHAR(10) = NULL;
EXEC sp_executesql #sql;
SELECT ##ROWCOUNT;
I would expect to get 0, maybe even NULL would make sense. But I don't get either, I get 1. Why is 1 row affected by executing a NULL query? If I pass in a "proper" (non_NULL) query then it works fine.
Background (for those that care): this is from a process that is supposed to generate some dynamic SQL to update one row and ONLY one row. I need to check that 1 row has been affected, not 0 or 2 or more than 2. It worked fine until somehow a NULL SQL statement managed to be generated, and this was seen as a success - oops!
The actual fix will be to check the SQL is non-NULL before running it, and treat a NULL statement the same way as a result other than 1. But I was still curious why it behaved this way.
It's because you're assign NULL to your variable. Statements that make a simple assignment always set the ##ROWCOUNT value to 1.
See the example below. Because management studio can run its own queries on the connection and mess with the ##ROWCOUNT value it starts off selecting an empty result set to ensure the initial ##ROWCOUNT value is zero.
When there is no assignment the SELECT ##ROWCOUNT returns 0 (the initial value has not been modified). Otherwise it returns 1
/*Ensure ##ROWCOUNT starts off at 0*/
SELECT 1 WHERE 1 = 0;
DECLARE #sql NVARCHAR(10);
EXEC sp_executesql #sql;
SELECT ##ROWCOUNT;
GO
/*Ensure ##ROWCOUNT starts off at 0*/
SELECT 1 WHERE 1 = 0;
DECLARE #sql NVARCHAR(10) = NULL;
EXEC sp_executesql #sql;
SELECT ##ROWCOUNT;
You can also try similar with a non zero initial value:
/*Ensure ##ROWCOUNT starts off at 3*/
SELECT 1 UNION SELECT 2 UNION SELECT 3
DECLARE #sql NVARCHAR(10);
EXEC sp_executesql #sql;
SELECT ##ROWCOUNT; --Returns 3
GO
/*Ensure ##ROWCOUNT starts off at 3*/
SELECT 1 UNION SELECT 2 UNION SELECT 3
DECLARE #sql NVARCHAR(10) = NULL;
EXEC sp_executesql #sql;
SELECT ##ROWCOUNT; --Returns 1
Please note that my answer was written purely from my experience of SQL Server Management Studio and does not accurately explain this behaviour. Martin Smith has explained why this isn't true in a comment below.
It looks like sp_executesql doesn't run at all with a null parameter, perhaps as a failsafe.
Try running "SELECT ##ROWCOUNT" alone in a batch and you'll see that it returns 1, regardless of the fact there is no current rowcount. It seems likely that 1 is the default return value and that's why you're seeing this.

Why is the ##ROWCOUNT variable returning zero after the IF statement

This is a simple code referring to the pubs database. The SELECT statement returns 10 records so ##ROWCOUNT variable should be set to 10. But how come in the message window, it says '0 records found'. Is there a reason why after an IF statement ##ROWCOUNT is set to 0?
If I put SELECT ##ROWCOUNT right after the WHERE statement, the ##ROWCOUNT variable is at 10. But it changes after executing the IF STATEMENT.
SELECT *
FROM pubs.dbo.employee
WHERE pub_id ='0877'
IF ##ROWCOUNT > 0
PRINT CONVERT(CHAR(2), ##ROWCOUNT) + ' records found'
ELSE
PRINT 'No records found'
##ROWCOUNT returns the row count for the last statement. It is highly volatile. So, basically, anything can reset it.
If you care about it, assign it to a parameter immediately!
DECLARE #ROWCNT INT;
SELECT * FROM pubs.dbo.employee WHERE pub_id = '0877';
SET #ROWCNT = ##ROWCOUNT;
Then use the parameter value.

SQL Stored Procedure - Understanding SQL Statement

New to stored procedures. Can anyone explain the following SQL sample which appears at the start of a stored procedure?
Begin/End - Encloses a series of SQL statements so that a group of SQL statements can be executed
SET NOCOUNT ON - the count (indicating the number of rows affected by a SQL statement) is not returned.
DECLARE - setting local variables
While - loops round
With - unsure
Update batch - unsure
SET #Rowcount = ##ROWCOUNT; - unsure
BEGIN
SET NOCOUNT ON;
--UPDATE, done in batches to minimise locking
DECLARE #Batch INT= 100;
DECLARE #Rowcount INT= #Batch;
WHILE #Rowcount > 0
BEGIN
WITH t
AS (
SELECT [OrganisationID],
[PropertyID],
[QuestionID],
[BaseAnsweredQuestionID]
FROM dbo.Unioned_Table
WHERE organisationid = 1),
s
AS (
SELECT [OrganisationID],
[PropertyID],
[QuestionID],
[BaseAnsweredQuestionID]
FROM dbo.table
WHERE organisationid = 1),
batch
AS (
SELECT TOP (#Batch) T.*,
s.BaseAnsweredQuestionID NewBaseAnsweredQuestionID
FROM T
INNER JOIN s ON t.organisationid = s.organisationid
AND t.PropertyID = s.PropertyID
AND t.QuestionID = s.QuestionID
WHERE t.BaseAnsweredQuestionID <> s.BaseAnsweredQuestionID)
UPDATE batch
SET
BaseAnsweredQuestionID = NewBaseAnsweredQuestionID
SET #Rowcount = ##ROWCOUNT;
END;
The clue is in the comment --UPDATE, done in batches to minimise locking.
The intent is to update dbo.table's column BaseAnsweredQuestionID with the equivalent column from dbo.Unioned_Table, in batches of 100. The comment suggests the batching logic is necessary to prevent locking.
In detail:
DECLARE #Batch INT= 100; sets the batch size.
DECLARE #Rowcount INT= #Batch; initializes the loop.
WHILE #Rowcount > 0 starts the loop. #Rowcount will become zero when the update statement affects no rows (see below).
with a as () is a common table expression (commonly abbreviated to CTE) - it creates a temporary result set which you can effectively treat as a table. The next few queries define CTEs t, s and batch.
CTE batch contains just 100 rows by using the SELECT TOP (#Batch) term - it selects a random 100 rows from the two other CTEs.
The next statement:
UPDATE batch
SET BaseAnsweredQuestionID = NewBaseAnsweredQuestionID
SET #Rowcount = ##ROWCOUNT
updates the 100 rows in the batch CTE (which in turn is a join on two other CTEs), and populates the loop variable #Rowcount with the number of rows affected by the update statement (##ROWCOUNT). If there are no matching rows, ##ROWCOUNT becomes zero, and thus the loop ends.

Why PRINT affects ##rowcount

I recently noticed that while trying to loop through the rows of a table my loop wouldn't run based on if I had a PRINT statement before the the loop condition involving ##ROWCOUNT.
declare #s int;
select top 1 #s=sequence from myTable order by sequence;
while ##rowcount > 0
begin
print #s
select top 1 #s=sequence from myTable where sequence > #s order by sequence;
end
print #s
The above code prints what is expected; a sequence of numbers for each row in the table, but,
declare #s int;
select top 1 #s=sequence from myTable order by sequence;
print #s
while ##rowcount > 0
begin
print #s
select top 1 #s=sequence from myTable where sequence > #s order by sequence;
end
print #s
only prints the first value of sequence twice (one for each PRINT outside of the loop).
I tried reading up on the PRINT statement but found nothing on it affecting ##ROWCOUNT.
My question is, why does this PRINT affect ##ROWCOUNT and why isn't it more clearly documented because this can cause some bugs which are hard to debug?
UPDATE
After more research I did find
Statements such as USE, SET , DEALLOCATE CURSOR, CLOSE CURSOR, PRINT, RAISERROR, BEGIN TRANSACTION, or COMMIT TRANSACTION reset the ROWCOUNT value to 0.
from Microsoft's ##ROWCOUNT docs
##ROWCOUNT always refers to the previous executed statement, even print.
That is why code using ##ROWCOUNT almost always assigns the value to a variable:
declare #s int;
declare #rowcnt int;
select top 1 #s = sequence from myTable;
set #rowcnt = ##ROWCOUNT;
while #rowcnt> 0
. . .

how to set rowcount to zero in SQL

anyone have an idea how to set rowcount to zero again in SQL.
I have used rowcount to fetch the number of records inserted in the insert statement. I have to use rowcount again to find the rows updated. So i am trying to reset the rowcount again to zero.
code will be look this :
INSERT INTO Table A ......
INSERT INTO statistics (id,inserted_records ) values (1,##rowcount)
---some operations--
Update Table A ....
Update statistics set updated_records=##rowcount where id=
select ##rowcount
only returns the row count from the most recent statement. It doesn't need to be reset.. Executing another statement will automatically reset it.
If for some bizarre reason, you want to make ##rowcount return 0, execute a query that will return 0 rows.
select 1 where 2=3
You can prove this like so.
declare #t table (i int)
declare #stats table(rc int)
insert #t values (1),(2),(3)
-- rowcount is 3
insert #stats values (##rowcount)
-- rowcount is 1
update #t set i=5 where i=4
select ##ROWCOUNT -- rowcount will be 0
#bibinmatthew ##ROWCOUNT automatically resets whenever you do another transaction.
What I would consider doing is
declare #rowsAffected int
insert into table A ...
select #rowsAffected = ##ROWCOUNT
insert into table statistics (id, inserted_records) values (iID, #rowsAffected)
declare #rowsAffected int
update table A ...
select #rowsAffected = ##ROWCOUNT
update table statistics set updated_records = #rowsAffected where id = iID
This way, you are not having to deal directly with the ##ROWCOUNT variable. I have created the #rowsAffected twice because I am assuming that you have the insert and the update scripts in different stored proc's
Information about rowcount are here:
http://technet.microsoft.com/en-us//library/ms187316.aspx
Statements such as USE, SET , DEALLOCATE CURSOR, CLOSE CURSOR,
BEGIN TRANSACTION or COMMIT TRANSACTION reset the ROWCOUNT value to 0
So maybe you can put the statements into a transaction, so they are better isolated against each other.
And you can always just send a request that does not update anything, like UPDATE mytable SET x=1 WHERE 0=1;