Syntax Trigger after update rollback - sql

I'm searching for a correct syntaxt for a after update trigger with a rollback:
ALTER TRIGGER UpdatePlayer ON Players
AFTER UPDATE
AS
BEGIN
BEGIN TRY
IF EXISTS(select * from PlayerContract where id in (select id from deleted))
BEGIN
RAISERROR ('No update',16,1)
ROLLBACK
END
ELSE
BEGIN
PRINT 'UPDATE'
END
END TRY
BEGIN CATCH
ROLLBACK
DECLARE #ErrorMessage VARCHAR(500)
SELECT #ErrorMessage = ERROR_MESSAGE()
RAISERROR (#ErrorMessage, 16, 1)
END CATCH
END
When i try a insert, i get the follow error:
Msg 3609, Level 16, State 1, Line 1
The transaction ended in the trigger. The batch has been aborted.

Related

Result from job procedure with multiple internal procedures

I have one procedure with multiple internal procedures. The main procedure is with try-catch block and transaction in while expression. Each of the internal procedures has no transaction and no try-catch block.
My question is if procedure3 crashes with an error, will the whole transaction be rolled back and procedure1 and procedure2 will not have executed? In this case what will XACT_STATE() do?
It looks like that:
DECLARE #param int;
CREATE TABLE #table (id int);
INSERT INTO #table (id)
SELECT SubmissionDataID
FROM [sob].[SubmissionData]
WHERE [sob].[SubmissionData].SubmissionStatusID = 9
AND [sob].[SubmissionData].[IsLast] = 1;
WHILE EXISTS (SELECT TOP 1 id FROM #table)
BEGIN
BEGIN TRY
BEGIN TRANSACTION
SELECT TOP 1 #param=id FROM #table
EXEC procedure1 (#param);
EXEC procedure2 (#param);
EXEC procedure3 (#param);
EXEC procedure4 (#param);
EXEC procedure5 (#param);
EXEC procedure6 (#param);
EXEC procedure7 (#param);
DELETE FROM #table
WHERE id = #param
COMMIT TRANSACTION
END TRY
BEGIN CATCH
DELETE FROM #table
WHERE id = #param;
SELECT
ERROR_MESSAGE(), ERROR_SEVERITY(), ERROR_STATE();
-- Transaction uncommittable
IF (XACT_STATE()) = -1
BEGIN
ROLLBACK TRANSACTION
PRINT 'INFO: TRANSACTION has been ROLLBACKED!';
THROW;
END
-- Transaction committable
IF (XACT_STATE()) = 1
COMMIT TRANSACTION
END CATCH
END --end while
DROP TABLE #table;
END
/* ************ END OF PROCEDURE ************ */
I'm not sure how XACT_STATE() works.

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