Rollback multiple transactions on try catch - sql-server

Hi i'm learning transactions and i have difficulty trying to understand how do i use rollback when i have multiple transactions with different names. I'm using a try catch to call rollback in case an error occurs
CREATE PROCEDURE InsertFromPDP
AS
BEGIN
DECLARE #tempTable TABLE
(
Id INT PRIMARY KEY,
Referencia VARCHAR(15),
UAP NVARCHAR(20),
ConsumoInicialWeek01 FLOAT,
ConsumoInicialWeek02 FLOAT,
Stock INT,
PecasPorCaixa INT,
NumTurnos INT DEFAULT 3,
NumPab INT DEFAULT 6,
AlcanceAbastecimento INT DEFAULT 3,
QtdMin INT DEFAULT 4,
QtdMax INT DEFAULT 12,
NumDias INT DEFAULT 5
UNIQUE (Id)
)
INSERT INTO
#tempTable
(
Id,
Referencia,
UAP,
ConsumoInicialWeek01,
ConsumoInicialWeek02,
Stock,
PecasPorCaixa
)
SELECT
*
FROM
viewConsumoPDP
BEGIN TRY
BEGIN TRAN InsertNotExistsReferenciasFromPDP;
INSERT INTO
Parametros
SELECT
M.Referencia,
M.UAP,
M.NumTurnos,
M.NumPab,
M.AlcanceAbastecimento,
M.QtdMin,
M.QtdMax,
M.NumDias
FROM
#tempTable M
WHERE
NOT EXISTS
(
SELECT
*
FROM Parametros P
WHERE
M.Referencia <> P.Referencia
AND
M.UAP <> P.UAP
)
BEGIN TRAN InsertConsumoFromPDP
-- TODO--
COMMIT InsertNotExistsReferenciasFromPDP
COMMIT InsertConsumoFromPDP
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
ROLLBACK InsertNotExistsReferenciasFromPDP
ROLLBACK InsertConsumoFromPDP
-- RAISE ERROR --
END CATCH
END
Is there a way to rollback all of the named transactions in just one command without specifying ROLLBACK + Name of the transaction?

ROLLBACK TRANSACTION without a savepoint_name or transaction_name rolls back to the beginning of the transaction.
You can find more details at: https://learn.microsoft.com/en-us/sql/t-sql/language-elements/rollback-transaction-transact-sql?view=sql-server-2017

Related

How to use BEGIN TRANSACTION with while loop in SQL Server?

How to use BEGIN TRANSACTION with while loop in SQL Server?
This query never finishes perhaps because it stops and look for COMMIT TRANSACTION after inserting one row (when #cnt = 1) but I don't want to COMMIT TRANSACTION because I want to see results before committing.
BEGIN TRANSACTION
DECLARE #cnt INT = 0;
WHILE #cnt <= 100
BEGIN
DECLARE #offset INT = 1
INSERT INTO totalSales (col1, col2)
SELECT
'Col1', ROW_NUMBER() OVER (ORDER BY col2) + #offset
FROM
sales
SET #cnt = #cnt + 1;
END;
So how I can check result before commit in while loop?
You should create a BEGIN TRAN outer (general), and inside loop while create a BEGIN TRAN inner (with a trans name).
Inside loop, if are conditions to rollbacks only for this iteration i use SAVE TRAN savepoint for not lose previous trans.
I 've created an example tests in loop while with conditional inserts and rollback savepoint:
declare #num int
set #num = 0
--drop table #test
create table #test (
valor Varchar(100)
)
begin tran
while #num <= 5
begin
begin transaction tran_inner
insert into #test (valor) values ('INSERT 1 INNER -> ' + convert(varchar(10),#num))
save transaction sv_inner
insert into #test (valor) values ('INSERT 2 EVEN - SAVEPOINT -> ' + convert(varchar(10),#num))
if #num % 2 = 0 begin
commit transaction sv_inner
end else begin
rollback transaction sv_inner
end
insert into #test (valor) values ('INSERT 3 INNER -> ' + convert(varchar(10),#num))
set #num = #num + 1
if ##trancount > 0 begin
commit transaction tran_inner
end
end
select valor from #test;
if ##trancount > 0 begin
commit tran
end
Return rows: 1, 2 if iteration even, and 3.
In the same batch (within the same transaction) you can simply issue a SELECT command to see the updated content of the table. Changes will be persisted when the COMMIT TRANSACTION statement is executed or reverted on ROLLBACK.
CREATE TABLE test (id INT IDENTITY(1,1), x VARCHAR(32));
GO
BEGIN TRANSACTION;
INSERT INTO test (x) VALUES ('a');
INSERT INTO test (x) VALUES ('b');
SELECT * FROM test;
ROLLBACK TRANSACTION;
Example: http://sqlfiddle.com/#!6/e4910/2
Alternatively you can use the INSERT INTO .. OUTPUT construct to output the result of the INSERT statement.
Docs: https://learn.microsoft.com/en-us/sql/t-sql/queries/output-clause-transact-sql
Outside the batch (using a second connection), you can use READ UNCOMMITTED isolation level to be able to read records not committed yet.
Docs: https://technet.microsoft.com/en-us/library/ms189122(v=sql.105).aspx
If you are saying it never finishes it sounds to me like you are getting some blocking going on because that loop runs just fine.
https://www.mssqltips.com/sqlservertip/2429/how-to-identify-blocking-in-sql-server/
I HIGHLY recommend using Adam Machanic's sp_WhoIsActive for this as well: http://whoisactive.com

Identity key counter increment by one although it is in TRY Catch and Transaction is roll-backed ? SSMS 2008

Identity counter increment by one although it is in TRY Catch and Transaction is roll-backed ? SSMS 2008 is there any way i can stop it +1 or rollback it too.
In order to understand why this happened, Let's execute below sample code first-
USE tempdb
CREATE TABLE dbo.Sales
(ID INT IDENTITY(1,1), Address VARCHAR(200))
GO
BEGIN TRANSACTION
INSERT DBO.Sales
( Address )
VALUES ( 'Dwarka, Delhi' );
ROLLBACK TRANSACTION
Now, Execution plan for above query is-
The second last operator from right Compute Scalar is computing value for [Expr1003]=getidentity((629577281),(2),NULL) which is IDENTITY value for ID column. So this clearly indicates that IDENTITY values are fetched & Incremented prior to Insertion (INSERT Operator). So its by nature that even transaction rollback at later stage once created IDENTITY value is there.
Now, in order to reseed the IDENTITY value to Maximum Identity Value present in table + 1, you need sysadmin permission to execute below DBCC command -
DBCC CHECKIDENT
(
table_name
[, { NORESEED | { RESEED [, new_reseed_value ] } } ]
)
[ WITH NO_INFOMSGS ]
So the final query should include below piece of code prior to rollback statement:-
-- Code to check max ID value, and verify it again IDENTITY SEED
DECLARE #MaxValue INT = (SELECT ISNULL(MAX(ID),1) FROM dbo.Sales)
IF #MaxValue IS NOT NULL AND #MaxValue <> IDENT_CURRENT('dbo.Sales')
DBCC CHECKIDENT ( 'dbo.Sales', RESEED, #MaxValue )
--ROLLBACK TRANSACTION
So it is recommended to leave it on SQL Server.
You are right and the following code inserts record with [Col01] equal to 2:
CREATE TABLE [dbo].[DataSource]
(
[Col01] SMALLINT IDENTITY(1,1)
,[Col02] TINYINT
);
GO
BEGIN TRY
BEGIN TRANSACTION;
INSERT INTO [dbo].[DataSource] ([Col02])
VALUES (1);
SELECT 1/0
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
BEGIN
ROLLBACK TRANSACTION
END;
END CATCH;
GO
INSERT INTO [dbo].[DataSource] ([Col02])
VALUES (1);
SELECT *
FROM [dbo].[DataSource]
This is by design (as you can see in the documentation:
Consecutive values after server restart or other failures –SQL Server
might cache identity values for performance reasons and some of the
assigned values can be lost during a database failure or server
restart. This can result in gaps in the identity value upon insert. If
gaps are not acceptable then the application should use its own
mechanism to generate key values. Using a sequence generator with the
NOCACHE option can limit the gaps to transactions that are never
committed.
I try using NOCACHE sequence but it does not work on SQL Server 2012:
CREATE TABLE [dbo].[DataSource]
(
[Col01] SMALLINT
,[Col02] TINYINT
);
CREATE SEQUENCE [dbo].[MyIndentyty]
START WITH 1
INCREMENT BY 1
NO CACHE;
GO
BEGIN TRY
BEGIN TRANSACTION;
INSERT INTO [dbo].[DataSource] ([Col01], [Col02])
SELECT NEXT VALUE FOR [dbo].[MyIndentyty], 1
SELECT 1/0
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
BEGIN
ROLLBACK TRANSACTION
END;
END CATCH;
GO
INSERT INTO [dbo].[DataSource] ([Col01], [Col02])
SELECT NEXT VALUE FOR [dbo].[MyIndentyty], 1
SELECT *
FROM [dbo].[DataSource]
DROP TABLE [dbo].[DataSource];
DROP SEQUENCE [dbo].[MyIndentyty];
You can use MAX to solve this:
CREATE TABLE [dbo].[DataSource]
(
[Col01] SMALLINT
,[Col02] TINYINT
);
BEGIN TRY
BEGIN TRANSACTION;
DECLARE #Value SMALLINT = (SELECT MAX([Col01]) FROM [dbo].[DataSource]);
INSERT INTO [dbo].[DataSource] ([Col01], [Col02])
SELECT #Value, 1
SELECT 1/0
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
BEGIN
ROLLBACK TRANSACTION
END;
END CATCH;
GO
DECLARE #Value SMALLINT = ISNULL((SELECT MAX([Col01]) FROM [dbo].[DataSource]), 1);
INSERT INTO [dbo].[DataSource] ([Col01], [Col02])
SELECT #Value, 1
SELECT *
FROM [dbo].[DataSource]
DROP TABLE [dbo].[DataSource];
But you must pay attentions to your isolation level for potential issues:
If you want to insert many rows at the same time, do the following:
get the current max value
create table where to store the rows (that are going to be inserted) generating ranking (you can use identity column, you can use ranking function) and adding the max value to it
insert the rows

Insertion transaction- rollback SQL

I am new to databases and i try to create a stored procedure that inserts data in tables that are in a many to many relation.If any part of the operation fails then it must try to recover as much as possible from the entire operation. For example, if one wants to create a record regarding publishers and books and succeeds creating the publisher but fails with the book, then it should roll back
the creation of the book, but not of the publisher.
My code looks like this:
BEGIN TRY
BEGIN TRANSACTION
DECLARE #serviciuKey int
DECLARE #specializareKey int
IF NOT EXISTS (SELECT denumire, moneda, pret FROM Serviciu where denumire=#denumire and moneda=#moneda and pret=#pret)
BEGIN
INSERT INTO Serviciu ( denumire, moneda, pret)
VALUES (#denumire, #moneda, #pret)
END
SET #serviciuKey=##IDENTITY
SAVE TRANSACTION savepoint
IF NOT EXISTS (SELECT denumire, descriere FROM Specializare where denumire=#denumire_spec AND descriere=#descriere)
BEGIN
INSERT INTO Specializare( denumire, descriere)
VALUES (#denumire_spec, #descriere)
END
SET #specializareKey=##IDENTITY
SAVE TRANSACTION savepoint
IF NOT EXISTS (SELECT * FROM Specializare_Serviciu where cod_specializare=#specializareKey and cod_serviciu=#serviciuKey)
BEGIN
INSERT INTO Specializare_Serviciu( cod_specializare, cod_serviciu)
VALUES (#specializareKey, #serviciuKey)
END
SAVE TRANSACTION savepoint
COMMIT TRANSACTION
END TRY
BEGIN CATCH
IF ##trancount > 0 ROLLBACK TRANSACTION savepoint
DECLARE #msg nvarchar(2048) = error_message()
RAISERROR (#msg, 16, 1)
RETURN 55555
END CATCH
When i execute the procedure, i have this error:
Msg 3931, Level 16, State 1, Procedure AddData0, Line 76
The current transaction cannot be committed and cannot be rolled back to a savepoint. Roll back the entire transaction.
Also when i try to insert some data that already exists, it is inserted with another ID, which means that IF NOT EXIST statement is not working.
Any help, please?
Try this version:
DECLARE #serviciuKey int
DECLARE #specializareKey int
BEGIN TRY
BEGIN TRAN
IF NOT EXISTS (SELECT denumire, moneda, pret FROM Serviciu where denumire=#denumire and moneda=#moneda and pret=#pret)
BEGIN
INSERT INTO Serviciu ( denumire, moneda, pret)
VALUES (#denumire, #moneda, #pret)
END
SET #serviciuKey=scope_identity()
IF NOT EXISTS (SELECT denumire, descriere FROM Specializare where denumire=#denumire_spec AND descriere=#descriere)
BEGIN
INSERT INTO Specializare( denumire, descriere)
VALUES (#denumire_spec, #descriere)
END
SET #specializareKey=scope_identity()
IF NOT EXISTS (SELECT * FROM Specializare_Serviciu where cod_specializare=#specializareKey and cod_serviciu=#serviciuKey)
BEGIN
INSERT INTO Specializare_Serviciu( cod_specializare, cod_serviciu)
VALUES (#specializareKey, #serviciuKey)
END
COMMIT TRAN
END TRY
BEGIN CATCH
IF ##trancount > 0
ROLLBACK TRAN
DECLARE #msg nvarchar(2048) = error_message()
RAISERROR (#msg, 16, 1)
RETURN 55555
END CATCH

tsql mutiple inserts in a transaction with rollback

I have a stored procedure when exec it, it will insert into 2 tables several records.
let's say insert into author table a record and insert into book table several records(author's books).
how to make all insert in a transaction with rollback?
i read some articles/blogs, ##trancount/##error/XACT_STATE() makes me confused.
what's the best approach?
here's my proc:
CREATE PROCEDURE [dbo].[proc_addAuthors]
#bookid1 int, #bookid2 int, #bookid3 int, #bookid4 int, #bookid5 int,
#authInfo
AS
insert into author...(leave out params)
--get authorId
...
--insert books (leave out validation checks...)
insert into author2book(authorId, bookId) values(#authorid, #bookid1)
...
RETURN 0
If you just want to make sure that the inserts either both finishes succesfully, or both rolls back if an error occurs, you need to add the following to your stored procedure:
CREATE PROCEDURE [dbo].[proc_addAuthors]
#bookid1 int, #bookid2 int, #bookid3 int, #bookid4 int, #bookid5 int,
#authInfo
AS
SET XACT_ABORT ON; -- Automatically rollback if an error occurs.
BEGIN TRANSACTION;
insert into author...(leave out params)
--get authorId
...
--insert books (leave out validation checks...)
insert into author2book(authorId, bookId) values(#authorid, #bookid1)
...
COMMIT TRANSACTION;
RETURN 0
CREATE PROCEDURE addTitle(#title_id VARCHAR(6), #au_id VARCHAR(11),
#title VARCHAR(20), #title_type CHAR(12))
AS
BEGIN TRAN
INSERT titles(title_id, title, type)
VALUES (#title_id, #title, #title_type)
IF (##ERROR <> 0) GOTO ERR_HANDLER
INSERT titleauthor(au_id, title_id)
VALUES (#au_id, #title_id)
IF (##ERROR <> 0) GOTO ERR_HANDLER
COMMIT TRAN
RETURN 0
ERR_HANDLER:
PRINT 'Unexpected error occurred!'
ROLLBACK TRAN
RETURN 1
Here is the way to go with modern TRY CATCH block to test the error
CREATE PROCEDURE [dbo].[proc_addAuthors]
#bookid1 int, #bookid2 int, #bookid3 int, #bookid4 int, #bookid5 int,
#authInfo
AS
BEGIN TRY
BEGIN TRANSACTION
insert into author...(leave out params)
--get authorId
...
--insert books (leave out validation checks...)
insert into author2book(authorId, bookId) values(#authorid, #bookid1)
...
COMMIT TRANSACTION
END TRY
BEGIN CATCH
SELECT ERROR_MESSAGE() as errorMessage -- for example
ROLLBACK
RETURN 1 -- for example
END CATCH
RETURN 0
Using Try..Catch block gives you access to error functions, which you can use to get detailed information a about the error.
CREATE PROCEDURE [dbo].[proc_addAuthors]
#bookid1 int, #bookid2 int, #bookid3 int, #bookid4 int, #bookid5 int,
#authInfo
AS
BEGIN
SET NOCOUNT ON;
DECLARE #NewAuthorID INT;
BEGIN TRY
BEGIN TRANSACTION
insert into author...(leave out params)
--get authorId
SET #NewAuthorID = SCOPE_IDENTITY(); --<-- Get new AuthorID generated by Identity column
--insert books (leave out validation checks...)
insert into author2book(authorId, bookId) --<-- use that NewAuthorID param here
values(#NewAuthorID, #bookid1)
...
COMMIT TRANSACTION
RETURN 0
END TRY
BEGIN CATCH
IF (##TRANCOUNT > 0)
ROLLBACK TRANSACTION
DECLARE #ErrorNum int
SELECT #ErrorNum = ERROR_NUMBER()
SELECT ERROR_MESSAGE() AS ErrorMessage,
ERROR_LINE() AS ErrorLine,
--<--.... ( Other Error Functions )
RETURN #ErrorNum
END CATCH
END

try catch doesn't work correct T-SQL

I would like to ask why (try catch) doesn't work correct because when is error in person 2 trigger showed problem. Don`t ask about different between table person and table person 2 this is only example to show problem.
CREATE TRIGGER Example
ON Person
after INSERT
AS
BEGIN TRY
INSERT INTO [Person2]
SELECT
inserted.Name,
inserted.Surname,
inserted.Age,
inserted.Street
FROM inserted
END TRY
BEGIN CATCH
END CATCH
Insert working correct if trigger is delated
INSERT INTO Person
Values
('Jhon', 'Kowalsky', '12', null)
error
(0 row(s) affected)
Msg 3616, Level 16, State 1, Line 1 An error was raised during trigger
execution. The batch has been aborted and the user transaction, if
any, has been rolled back.
table Person - here you can see one difference between person and person 2 Street allow nulls
table Person 2 - here you can see one difference between person and person 2 Street doesn't allow nulls
Try this one -
CREATE TRIGGER dbo.Example
ON dbo.Person
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON
SET XACT_ABORT ON
DECLARE #OwnTran BIT
SET #OwnTran = 0
IF ##TRANCOUNT = 0
BEGIN
SET #OwnTran = 1
BEGIN TRAN
END
BEGIN TRY
INSERT INTO dbo.Person2 (Name, Surname, Age, Street)
SELECT
i.Name
, i.Surname
, i.Age
, i.Street
FROM INSERTED i
END TRY
BEGIN CATCH
DECLARE #em NVARCHAR(MAX), #sev INT, #st INT
SELECT #em = ERROR_MESSAGE(),
#sev = ERROR_SEVERITY(),
#st = ERROR_STATE()
RAISERROR(#em, #sev, #st)
IF ##TRANCOUNT > 0
ROLLBACK TRAN
END CATCH
IF #OwnTran = 1 AND ##TRANCOUNT > 0
BEGIN
COMMIT TRAN
END
END

Resources