What Order should I Call ##ROWCOUNT/##ERROR - sql

I am inserting a number or rows into a table using INSERT with SELECT.
After the transaction, I want to store both the ##ROWCOUNT and ##ERROR values into locallay declared variables.
INSERT SubscriberList (PublicationId, SubscriberId)
SELECT #PublicationId, S.SubscriberId
FROM Subscribers S
SET #NoRows = ##ROWCOUNT
SET #ErrorCode = ##ERROR
I wasn't sure if this was valid in as much if I call one, will I negate the other?

Set them both at once:
SELECT #NoRows = ##ROWCOUNT, #ErrorCode = ##ERROR

In addition to #JNK's answer...
I never use ##ERROR now because of TRY/CATCH
BEGIN TRY
BEGIN TRAN
INSERT SubscriberList (PublicationId, SubscriberId)
SELECT #PublicationId, S.SubscriberId
FROM Subscribers S
SET #NoRows = ##ROWCOUNT
... do more inserts, updates etc
COMMIT TRAN
END TRY
BEGIN CATCH
ROLLBACK TRAN
SET #ErrorCode = ERROR_NUMBER()
RAISERROR ...
END CATCH

Related

In SQL rollback transaction, from where it rollbacks the state?

In SQL rollback transaction, from where it rollbacks the state? I mean where the data is stored so that rollback can take it back.
For Oracle, it is saving changes in redo log until commit. Every RDMS has own strategy.
you can use safe and easy code for run 100% (run all line of query) or no run any off them
|query 1| = like insert into or select or ...
|count of lines| = number off query
DECLARE #rowcount int set #rowcount = 0 ;
BEGIN TRANSACTION [Tran1]
BEGIN TRY
<Query 1> ; set #rowcount = (#rowcount + ##ROWCOUNT);
<Query 2> ; set #rowcount = (#rowcount + ##ROWCOUNT);
...
IF #rowcount = <count of lines>
COMMIT TRANSACTION[Tran1]
ELSE
ROLLBACK TRANSACTION[Tran1]
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION[Tran1]
END CATCH
for example this code run 2 insert into line query but or run all of him or no run anything and ROLLBACK
DECLARE #rowcount int set #rowcount = 0 ;
BEGIN TRANSACTION [Tran1]
BEGIN TRY
insert into [database].[dbo].[tbl1] (fld1) values('1') ;
set #rowcount = (#rowcount + ##ROWCOUNT);
insert into [database].[dbo].[tbl2] (fld1) values('2') ;
set #rowcount = (#rowcount + ##ROWCOUNT);
IF #rowcount = 2
COMMIT TRANSACTION[Tran1]
ELSE
ROLLBACK TRANSACTION[Tran1]
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION[Tran1]
END CATCH

Stored Procedure doesn't return affected rows after exception handling

I have added exception handling in my stored procedure as below.
ALTER PROCEDURE [dbo].[BUDGETUPDATE]
#DistrictID int
AS
BEGIN
SET NOCOUNT ON ;
BEGIN TRY
BEGIN TRAN
UPDATE bud
SET bud.BudgetStateID = #BudgetStateID
FROM [dbo].[BudgetOffice] bud
INNER JOIN [dbo].[vw_Office] vw
ON (vw.OfficeID = bud.OfficeID)
WHERE vw.DistrictID = #DistrictID
IF ##ERROR = 0
BEGIN
COMMIT TRAN;
SELECT ##ROWCOUNT AS AffectedRow;
END
END TRY
BEGIN CATCH
SELECT ##ERROR AS ERROR
ROLLBACK TRAN;
END CATCH
SET NOCOUNT OFF ;
END
I need to return the number of affected rows using ##ROWCOUNT. But this stored procedure always returns rowcount as 0. Any reason for this. Do I need to write the ##rowcount statement right after update?
You need to select ##ROWCOUNT after your UPDATE statement. As per the documentation:
Statements such as USE, SET , DEALLOCATE CURSOR, CLOSE CURSOR,
BEGIN TRANSACTION or COMMIT TRANSACTION reset the ROWCOUNT value to 0.
Since your ##ROWCOUNT is after the COMMIT TRAN, ##ROWCOUNT returns 0.
You need to store result of global variables in local variable because it will change after next instruction like:
ALTER PROCEDURE [dbo].[BUDGETUPDATE]
#DistrictID int
AS
BEGIN
SET NOCOUNT ON ;
DECLARE #rowcount INT, #error INT;
BEGIN TRY
BEGIN TRAN
UPDATE bud
SET bud.BudgetStateID = #BudgetStateID
FROM [dbo].[BudgetOffice] bud
JOIN [dbo].[vw_Office] vw
ON vw.OfficeID = bud.OfficeID
WHERE vw.DistrictID = #DistrictID;
SELECT #error = ##ERROR, #rowcount = ##ROWCOUNT;
IF #error = 0
BEGIN
COMMIT TRAN;
SELECT #rowcount AS AffectedRow;
END
END TRY
BEGIN CATCH
SELECT ##ERROR AS ERROR
ROLLBACK TRAN;
END CATCH
END
Or even better resign for using ##ERROR in TRY CATCH block:
ALTER PROCEDURE [dbo].[BUDGETUPDATE]
#DistrictID int
AS
BEGIN
SET NOCOUNT ON ;
BEGIN TRY
BEGIN TRAN
UPDATE bud
SET bud.BudgetStateID = #BudgetStateID
FROM [dbo].[BudgetOffice] bud
JOIN [dbo].[vw_Office] vw
ON vw.OfficeID = bud.OfficeID
WHERE vw.DistrictID = #DistrictID;
SELECT ##ROWCOUNT AS AffectedRow;
COMMIT TRAN;
END TRY
BEGIN CATCH
SELECT ERROR_MESSAGE() AS ERROR
ROLLBACK TRAN;
END CATCH
END
And where is #BudgetStateID defined?

ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION

I've tried putting the COMMIT TRAN in a if else loop, and I'm still getting this error.
I have to enroll a student in a class. If the number of seats after enrollment falls in negative, I have to reverse it and print a message saying can't enroll. I have put other error messages just to see how transactions work.
CREATE PROCEDURE dbo.EnrollStudent ( #CourseID AS INTEGER,
#StudentID AS VARCHAR(20) ) AS
BEGIN
DECLARE #StatusID INTEGER
DECLARE #Status VARCHAR(50)
DECLARE #CurrentSeats INTEGER
DECLARE #ErrorCode INTEGER
SET #StatusID=0
IF EXISTS (SELECT 1
FROM dbo.CourseEnrollment
WHERE dbo.CourseEnrollment.CourseId=#CourseID AND dbo.CourseEnrollment.StudentId=#StudentID )
BEGIN
BEGIN TRAN Tr1
SET #StatusID = 1
SELECT #ErrorCode=##ERROR
IF (#ErrorCode<>0) GOTO OTHERPROBLEM
ELSE
COMMIT TRAN Tr1
END
IF EXISTS ( SELECT 1
FROM dbo.CourseEnrollment
FULL OUTER JOIN dbo.Courses
ON dbo.Courses.CourseId=#CourseID
WHERE dbo.CourseEnrollment.StudentId<>#StudentID AND dbo.Courses.Faculty IS NULL )
BEGIN
BEGIN TRAN Tr2
SET #StatusID=2
SELECT #ErrorCode=##ERROR
IF (#ErrorCode<>0) GOTO OTHERPROBLEM2
ELSE
COMMIT TRAN Tr2
END
IF #StatusID=0
BEGIN
IF EXISTS ( SELECT 1
FROM dbo.Courses
WHERE dbo.Courses.CourseId=#CourseID AND dbo.Courses.Faculty IS NOT NULL )
BEGIN
BEGIN TRAN Tr3
SET #StatusID=3
BEGIN TRAN InsertingValues
INSERT INTO dbo.CourseEnrollment (dbo.CourseEnrollment.StudentId,dbo.CourseEnrollment.CourseId)
VALUES (#StudentID,#CourseID);
SELECT #ErrorCode=##ERROR
IF (#ErrorCode<>0) GOTO InsertProblem
ELSE
COMMIT TRAN InsertingValues
BEGIN TRAN UpdateCourses
UPDATE dbo.Courses
SET OpenSeats = OpenSeats-1
WHERE dbo.Courses.CourseId = #CourseID
SELECT #ErrorCode=##ERROR
IF (#ErrorCode<>0) GOTO UpdateProblem
ELSE
COMMIT TRAN UpdateCourses
SELECT #CurrentSeats=OpenSeats
FROM dbo.Courses
WHERE dbo.Courses.CourseId = #CourseID
IF (#CurrentSeats<0) GOTO PROBLEM
ELSE
COMMIT TRAN Tr3
END
END
OTHERPROBLEM:
BEGIN
PRINT 'Unable to set status'
ROLLBACK TRAN
END
OTHERPROBLEM2:
BEGIN
PRINT 'Unable to set status'
ROLLBACK TRAN
END
UpdateProblem:
BEGIN
PRINT 'Not able to update values'
ROLLBACK TRAN InsertingValues
END
InsertProblem:
BEGIN
PRINT 'Not able to insert'
ROLLBACK TRAN InsertingValues
END
PROBLEM:
BEGIN
PRINT 'Seats Full!'
ROLLBACK TRAN
END
IF #StatusID = 1
BEGIN
SET #Status = 'The Student is already enrolled'
END;
ELSE IF #StatusID = 2
BEGIN
SET #Status = 'Cannot enroll until faculty is selected'
END
ELSE IF #StatusID = 3
BEGIN
SET #Status = 'Student Enrolled'
END
SELECT #Status
END;
This correctly updated the tables, but is giving the following errors:
(1 row(s) affected)
(1 row(s) affected)
Unable to set status
Msg 3903, Level 16, State 1, Procedure EnrollStudent, Line 101
The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION.
Unable to set status
Msg 3903, Level 16, State 1, Procedure EnrollStudent, Line 108
The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION.
Not able to update values
Msg 3903, Level 16, State 1, Procedure EnrollStudent, Line 115
The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION.
Not able to insert
Msg 3903, Level 16, State 1, Procedure EnrollStudent, Line 123
The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION.
Seats Full!
Msg 3903, Level 16, State 1, Procedure EnrollStudent, Line 131
The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION.
(1 row(s) affected)
The error you are getting is because you are rolling back without having an open transaction (you have either already committed or rolled-back). Consider cleaning up the structure of your stored proc, try executing your entire stored proc as one transaction, and then rolling back if an error occurs. You can also test if a rollback is required by checking if a transaction is open:
BEGIN TRANSACTION;
BEGIN TRY
--execute all your stored proc code here and then commit
COMMIT;
END TRY
BEGIN CATCH
--if an exception occurs execute your rollback, also test that you have had some successful transactions
IF ##TRANCOUNT > 0 ROLLBACK;
END CATCH
You need to specify the transaction name you want to rollback if it is named.
begin with that.
After that you could tell us wich transaction is failing (ensure that the transaction it is not being commited before).
BEGIN TRAN Tr1
-- your code
ROLLBACK TRAN Tr1
In my case I was using pyodbc to access SQL Server. Even a simple "SELECT * from table" was resulting in an error like "ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION".
Turned out to be permission related.
Just from a quick look - could it be because you have named your transaction when you start it (Tr1), but not referred to the name in your error handler?
DECLARE #Error varchar(max)
SET #Error = ''
BEGIN TRY
INSERT INTO OPERACION(CONTRATOID,FLUJO,MONTO,ID_ORIGINAL,ID_TRX,ESTADO,TIPO,REFERENCIA,
US_CREA,US_ACT,FEC_CRE,REQUEST,RESPONSE)
VALUES(#P_CONTRATOID,#P_FLUJO,#P_MONTO,#P_ID_ORIGINAL,#P_ID_TRX,#P_ESTADO,
#P_TIPO,#P_REFERENCIA,#P_US_CREA,#P_US_ACT,getdate(),#P_REQUEST,#P_RESPONSE)
END TRY
BEGIN CATCH
SELECT #Error = 'err: '+ ERROR_MESSAGE()
ROLLBACK ;
END CATCH
SELECT #Error
IF #FailureCount = 0
BEGIN
IF ##TRANCOUNT > 0
BEGIN
COMMIT TRAN a
END
END
ELSE
BEGIN
IF ##TRANCOUNT > 0
BEGIN
ROLLBACK TRAN a
END
END

Getting No of records inserted in Transaction

I need to get the no of records affected from the stored procedure. Based on this value I want to update the user whether the action got completed.
But my OUTPUT #RecordsAffected parameter is always 0
How to get the no of records inserted in the transaction?
Followed How can I get the number of records affected by a stored procedure? and http://rusanu.com/2009/06/11/exception-handling-and-nested-transactions/
This is my procedure
ALTER PROCEDURE [dbo].[SaveRoleFeatures]
(
#RoleId INT,
#SelectedFeatures AS IdInt READONLY,
#RecordsAffected INT OUTPUT
)
AS
BEGIN
set nocount on;
DECLARE #ErrorCode int
SELECT #ErrorCode = ##ERROR
declare #trancount int;
set #trancount = ##trancount;
BEGIN TRY
BEGIN TRAN
DELETE FROM RoleXFeature WHERE RoleIid= #RoleId
INSERT RoleXFeature
(
RoleIid,
FeatureId,
IsDeleted
)
SELECT
#RoleId,
Id,
0
FROM #SelectedFeatures
COMMIT TRAN
SET #RecordsAffected = ##ROWCOUNT
END TRY
BEGIN CATCH
declare #error int, #message varchar(4000), #xstate int;
select #error = ERROR_NUMBER(),
#message = ERROR_MESSAGE(), #xstate = XACT_STATE();
if #xstate = -1
rollback;
if #xstate = 1 and #trancount = 0
rollback
if #xstate = 1 and #trancount > 0
rollback transaction SaveRoleFeatures;
raiserror ('usp_my_procedure_name: %d: %s', 16, 1, #error, #message) ;
return;
END CATCH
END
You need to check ##ROWCOUNT before you commit.
As #GSerg noted, this is because ##rowcount is supposed to return number of rows affected by the last statement, and commit is a statement that is documented to set ##rowcount to 0.

SQL Server stored procedure restore my records if Insert failed

I want to know if there is a way to state rollback the delete if I can not insert.
Please advice.
Something like below.
BEGIN TRAN
Delete from MYTABLE where ID=#ID;
INSERT INTO MYTABLE (ID, NAME)
SELECT #ID, NAME
COMMIT
You can put your two statements into a TRY....CATCH block and only commit if both statements succeed:
BEGIN TRANSACTION
BEGIN TRY
DELETE FROM dbo.MYTABLE WHERE ID=#ID;
INSERT INTO dbo.MYTABLE (ID, NAME)
SELECT #ID, NAME
-- COMMIT only if both DELETE and INSERT worked ....
COMMIT TRANSACTION
END TRY
BEGIN CATCH
SELECT
ERROR_NUMBER() AS ErrorNumber,
ERROR_SEVERITY() AS ErrorSeverity,
ERROR_STATE() AS ErrorState,
ERROR_PROCEDURE() AS ErrorProcedure,
ERROR_LINE() AS ErrorLine,
ERROR_MESSAGE() AS ErrorMessage
-- ROLLBACK if either DELETE and INSERT failed ....
ROLLBACK TRANSACTION
END CATCH
Turn xact_abort on to rollback the transaction on any error.
SET XACT_ABORT ON;
BEGIN TRAN
Delete from MYTABLE where ID=#ID;
INSERT INTO MYTABLE (ID, NAME)
SELECT #ID, NAME
COMMIT TRAN
Here is another way to accomplish what you appear to be trying:
update myTable
set name = #name
where id = #id
BEGIN TRAN
Delete from MYTABLE where ID=#ID;
INSERT INTO MYTABLE (ID, NAME)
SELECT #ID, NAME
if ##error = 0 and ##trancount > 0
commit
else
rollback