From: trubar a on
Hi

Sorry for putting so many questions into one thread, but in my opinion
questions are somewhat related. If it’s a problem, I can break the
questions into several threads.


1) “Each COMMIT TRANSACTION issued when @@TRANCOUNT is greater than 1
simply decrements @@TRANCOUNT by 1.”

I assume calling rollback “inside” a nested transaction rolls back the
entire transaction and not just the nested one?

2) One of the optional arguments of BEGIN TRANSACTION statement is
also transaction_name parameter:

BEGIN { TRAN | TRANSACTION }
[ { transaction_name | @tran_name_variable }
[ WITH MARK [ 'description' ] ]
]
[ ; ]


“Although each COMMIT TRANSACTION statement has a transaction_name
parameter, there is no relationship between the COMMIT TRANSACTION and
BEGIN TRANSACTION statements. The transaction_name parameters are
simply readability aids to help the programmer ensure that the proper
number of commits are coded to decrement @@TRANCOUNT to 0 and thereby
commit the outer transaction.”

a) Does the above quote imply that transaction_name parameters are
always used just for readability?
b) But doesn’t the following quote imply that transaction_name
parameters are not used just for readability:

“If a transaction is rolled back to a savepoint, it must proceed to
completion with more Transact-SQL statements if needed and a COMMIT
TRANSACTION statement, or it must be canceled altogether by rolling
the transaction back to its beginning. To cancel an entire
transaction, use the form ROLLBACK TRANSACTION transaction_name. All
the statements or procedures of the transaction are undone.”


c) Anyways, I thought simply calling ROLLBACK / ROLLBACK TRANSACTION
will roll the entire transaction to its beginning, even if ROLLBACK is
called “inside” nested transaction? If so, why would we ever need to
call ROLLBACK TRANSACTION transaction_name instead of just calling
ROLLBACK?



3)
BEGIN TRY
DELETE HumanResources.JobCandidate
WHERE JobCandidateID = @InputCandidateID;
-- Get here if no errors; must commit
-- any transaction started in the
-- procedure, but not commit a transaction
-- started before the transaction was called.
IF @TranCounter = 0
-- @TranCounter = 0 means no transaction was
-- started before the procedure was called.
-- The procedure must commit the transaction
-- it started.
COMMIT TRANSACTION;
END TRY


The above code is an excerpt from the SQL code I’ve posted at the
bottom of this post. In any case, code shown in this excerpt calls
COMMIT TRANSACTION only if @TranCounter variable is equal to 0
( which means no transaction was started before the procedure was
called).

But doesn’t this checking only make sense if calling COMMIT
TRANSACTION would commit the entire transaction, even if @TranCounter
was greater than 0 ( @TranCounter being greater than 0 means that
@@TRANCOUNT > 1)? But as far as I know, if @@TRANCOUNT is greater than
1, then all that happens is that @@TRANCOUNT gets decremented by 1
( thus transaction doesn’t get committed ). The following quote also
suggests this:

“When used in nested transactions, commits of the inner transactions
do not free resources or make their modifications permanent. The data
modifications are made permanent and resources freed only when the
outer transaction is committed. Each COMMIT TRANSACTION issued when
@@TRANCOUNT is greater than 1 simply decrements @@TRANCOUNT by 1. When
@@TRANCOUNT is finally decremented to 0, the entire outer transaction
is committed.”


Example is taken from http://msdn.microsoft.com/en-us/library/ms188378.aspx
:


USE AdventureWorks;
GO
IF EXISTS (SELECT name FROM sys.objects
WHERE name = N'SaveTranExample')
DROP PROCEDURE SaveTranExample;
GO
CREATE PROCEDURE SaveTranExample
@InputCandidateID INT
AS
-- Detect if the procedure was called
-- from an active transaction and save
-- that for later use.
-- In the procedure, @TranCounter = 0
-- means there was no active transaction
-- and the procedure started one.
-- @TranCounter > 0 means an active
-- transaction was started before the
-- procedure was called.
DECLARE @TranCounter INT;
SET @TranCounter = @@TRANCOUNT;
IF @TranCounter > 0
-- Procedure called when there is
-- an active transaction.
-- Create a savepoint to be able
-- to roll back only the work done
-- in the procedure if there is an
-- error.
SAVE TRANSACTION ProcedureSave;
ELSE
-- Procedure must start its own
-- transaction.
BEGIN TRANSACTION;
-- Modify database.
BEGIN TRY
DELETE HumanResources.JobCandidate
WHERE JobCandidateID = @InputCandidateID;
-- Get here if no errors; must commit
-- any transaction started in the
-- procedure, but not commit a transaction
-- started before the transaction was called.
IF @TranCounter = 0
-- @TranCounter = 0 means no transaction was
-- started before the procedure was called.
-- The procedure must commit the transaction
-- it started.
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
-- An error occurred; must determine
-- which type of rollback will roll
-- back only the work done in the
-- procedure.
IF @TranCounter = 0
-- Transaction started in procedure.
-- Roll back complete transaction.
ROLLBACK TRANSACTION;
ELSE
-- Transaction started before procedure
-- called, do not roll back modifications
-- made before the procedure was called.
IF XACT_STATE() <> -1
-- If the transaction is still valid, just
-- roll back to the savepoint set at the
-- start of the stored procedure.
ROLLBACK TRANSACTION ProcedureSave;
-- If the transaction is uncommitable, a
-- rollback to the savepoint is not allowed
-- because the savepoint rollback writes to
-- the log. Just return to the caller, which
-- should roll back the outer transaction.

-- After the appropriate rollback, echo error
-- information to the caller.
DECLARE @ErrorMessage NVARCHAR(4000);
DECLARE @ErrorSeverity INT;
DECLARE @ErrorState INT;

SELECT @ErrorMessage = ERROR_MESSAGE();
SELECT @ErrorSeverity = ERROR_SEVERITY();
SELECT @ErrorState = ERROR_STATE();

RAISERROR (@ErrorMessage, -- Message text.
@ErrorSeverity, -- Severity.
@ErrorState -- State.
);
END CATCH
GO


Thank you
From: Erland Sommarskog on
trubar a (asponmynet(a)gmail.com) writes:
> 1) �Each COMMIT TRANSACTION issued when @@TRANCOUNT is greater than 1
> simply decrements @@TRANCOUNT by 1.�
>
> I assume calling rollback �inside� a nested transaction rolls back the
> entire transaction and not just the nested one?

Yes.

> a) Does the above quote imply that transaction_name parameters are
> always used just for readability?

Personally, I see little reason to name transaction - unless you use
the very special WITH MARK

> b) But doesn�t the following quote imply that transaction_name
> parameters are not used just for readability:
>
> �If a transaction is rolled back to a savepoint, it must proceed to
> completion with more Transact-SQL statements if needed and a COMMIT
> TRANSACTION statement, or it must be canceled altogether by rolling
> the transaction back to its beginning. To cancel an entire
> transaction, use the form ROLLBACK TRANSACTION transaction_name. All
> the statements or procedures of the transaction are undone.�

I don't think that is correct. To rollback to the beginning, ROLLBACK
TRANSACTION is sufficient.

> c) Anyways, I thought simply calling ROLLBACK / ROLLBACK TRANSACTION
> will roll the entire transaction to its beginning, even if ROLLBACK is
> called �inside� nested transaction? If so, why would we ever need to
> call ROLLBACK TRANSACTION transaction_name instead of just calling
> ROLLBACK?

The one situation you name a name after ROLLBACK TRANSACTION is when
you rollback to a *savepoint*. When you do this, you are still in a
transaction.

> The above code is an excerpt from the SQL code I�ve posted at the
> bottom of this post. In any case, code shown in this excerpt calls
> COMMIT TRANSACTION only if @TranCounter variable is equal to 0
> ( which means no transaction was started before the procedure was
> called).

I'm �n even more hurry now, but I'd say the example is hopelessly
overworked. While you can use savepoints, you don't have to. In my over
15 years with SQL Server, I think I've used SAVE TRANSACTION twice
in production code.



--
Erland Sommarskog, SQL Server MVP, esquel(a)sommarskog.se

Links for SQL Server Books Online:
SQL 2008: http://msdn.microsoft.com/en-us/sqlserver/cc514207.aspx
SQL 2005: http://msdn.microsoft.com/en-us/sqlserver/bb895970.aspx
SQL 2000: http://www.microsoft.com/sql/prodinfo/previousversions/books.mspx

From: Sylvain Lafontaine on
Most of your questions have already been answered by Erland, so I just want
to add a little explanation for the use of @TranCounter in the code example.
When you call a stored procedure, the transaction count when exiting the SP
must be the same as it was when calling the SP. This means that if a
transaction is already open when calling this SP, then you cannot perform a
rollback inside this SP because the rollback will set the transaction count
to 0 and therefore, the transaction count will be different when returning
from the SP and will generate an error.

This is why they are testing for the value @@TRANCOUNT at the beginning of
the SP: this is for determining if the SP has been called from inside a
running (open) transaction or not. If not, then the code start a
transaction and close it (commit or rollback) at the end of the SP; if yes,
then either you do nothing (you don't open a new transaction and you don't
make any commit or rollback at the end) or you create a saved point; which
can be seen as a mini-nested-transaction. This is the easiest way of
dealing with the problem of calling a SP from inside an open transaction or
not.

--
Sylvain Lafontaine, ing.
MVP - Windows Live Platform
Blog/web site: http://coding-paparazzi.sylvainlafontaine.com
Independent consultant and remote programming for Access and SQL-Server
(French)


"trubar a" <asponmynet(a)gmail.com> wrote in message
news:9f49d502-bb9c-4ce9-868d-13ed27a777f6(a)b18g2000yqb.googlegroups.com...
Hi

Sorry for putting so many questions into one thread, but in my opinion
questions are somewhat related. If it�s a problem, I can break the
questions into several threads.


1) �Each COMMIT TRANSACTION issued when @@TRANCOUNT is greater than 1
simply decrements @@TRANCOUNT by 1.�

I assume calling rollback �inside� a nested transaction rolls back the
entire transaction and not just the nested one?

2) One of the optional arguments of BEGIN TRANSACTION statement is
also transaction_name parameter:

BEGIN { TRAN | TRANSACTION }
[ { transaction_name | @tran_name_variable }
[ WITH MARK [ 'description' ] ]
]
[ ; ]


�Although each COMMIT TRANSACTION statement has a transaction_name
parameter, there is no relationship between the COMMIT TRANSACTION and
BEGIN TRANSACTION statements. The transaction_name parameters are
simply readability aids to help the programmer ensure that the proper
number of commits are coded to decrement @@TRANCOUNT to 0 and thereby
commit the outer transaction.�

a) Does the above quote imply that transaction_name parameters are
always used just for readability?
b) But doesn�t the following quote imply that transaction_name
parameters are not used just for readability:

�If a transaction is rolled back to a savepoint, it must proceed to
completion with more Transact-SQL statements if needed and a COMMIT
TRANSACTION statement, or it must be canceled altogether by rolling
the transaction back to its beginning. To cancel an entire
transaction, use the form ROLLBACK TRANSACTION transaction_name. All
the statements or procedures of the transaction are undone.�


c) Anyways, I thought simply calling ROLLBACK / ROLLBACK TRANSACTION
will roll the entire transaction to its beginning, even if ROLLBACK is
called �inside� nested transaction? If so, why would we ever need to
call ROLLBACK TRANSACTION transaction_name instead of just calling
ROLLBACK?



3)
BEGIN TRY
DELETE HumanResources.JobCandidate
WHERE JobCandidateID = @InputCandidateID;
-- Get here if no errors; must commit
-- any transaction started in the
-- procedure, but not commit a transaction
-- started before the transaction was called.
IF @TranCounter = 0
-- @TranCounter = 0 means no transaction was
-- started before the procedure was called.
-- The procedure must commit the transaction
-- it started.
COMMIT TRANSACTION;
END TRY


The above code is an excerpt from the SQL code I�ve posted at the
bottom of this post. In any case, code shown in this excerpt calls
COMMIT TRANSACTION only if @TranCounter variable is equal to 0
( which means no transaction was started before the procedure was
called).

But doesn�t this checking only make sense if calling COMMIT
TRANSACTION would commit the entire transaction, even if @TranCounter
was greater than 0 ( @TranCounter being greater than 0 means that
@@TRANCOUNT > 1)? But as far as I know, if @@TRANCOUNT is greater than
1, then all that happens is that @@TRANCOUNT gets decremented by 1
( thus transaction doesn�t get committed ). The following quote also
suggests this:

�When used in nested transactions, commits of the inner transactions
do not free resources or make their modifications permanent. The data
modifications are made permanent and resources freed only when the
outer transaction is committed. Each COMMIT TRANSACTION issued when
@@TRANCOUNT is greater than 1 simply decrements @@TRANCOUNT by 1. When
@@TRANCOUNT is finally decremented to 0, the entire outer transaction
is committed.�


Example is taken from http://msdn.microsoft.com/en-us/library/ms188378.aspx
:


USE AdventureWorks;
GO
IF EXISTS (SELECT name FROM sys.objects
WHERE name = N'SaveTranExample')
DROP PROCEDURE SaveTranExample;
GO
CREATE PROCEDURE SaveTranExample
@InputCandidateID INT
AS
-- Detect if the procedure was called
-- from an active transaction and save
-- that for later use.
-- In the procedure, @TranCounter = 0
-- means there was no active transaction
-- and the procedure started one.
-- @TranCounter > 0 means an active
-- transaction was started before the
-- procedure was called.
DECLARE @TranCounter INT;
SET @TranCounter = @@TRANCOUNT;
IF @TranCounter > 0
-- Procedure called when there is
-- an active transaction.
-- Create a savepoint to be able
-- to roll back only the work done
-- in the procedure if there is an
-- error.
SAVE TRANSACTION ProcedureSave;
ELSE
-- Procedure must start its own
-- transaction.
BEGIN TRANSACTION;
-- Modify database.
BEGIN TRY
DELETE HumanResources.JobCandidate
WHERE JobCandidateID = @InputCandidateID;
-- Get here if no errors; must commit
-- any transaction started in the
-- procedure, but not commit a transaction
-- started before the transaction was called.
IF @TranCounter = 0
-- @TranCounter = 0 means no transaction was
-- started before the procedure was called.
-- The procedure must commit the transaction
-- it started.
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
-- An error occurred; must determine
-- which type of rollback will roll
-- back only the work done in the
-- procedure.
IF @TranCounter = 0
-- Transaction started in procedure.
-- Roll back complete transaction.
ROLLBACK TRANSACTION;
ELSE
-- Transaction started before procedure
-- called, do not roll back modifications
-- made before the procedure was called.
IF XACT_STATE() <> -1
-- If the transaction is still valid, just
-- roll back to the savepoint set at the
-- start of the stored procedure.
ROLLBACK TRANSACTION ProcedureSave;
-- If the transaction is uncommitable, a
-- rollback to the savepoint is not allowed
-- because the savepoint rollback writes to
-- the log. Just return to the caller, which
-- should roll back the outer transaction.

-- After the appropriate rollback, echo error
-- information to the caller.
DECLARE @ErrorMessage NVARCHAR(4000);
DECLARE @ErrorSeverity INT;
DECLARE @ErrorState INT;

SELECT @ErrorMessage = ERROR_MESSAGE();
SELECT @ErrorSeverity = ERROR_SEVERITY();
SELECT @ErrorState = ERROR_STATE();

RAISERROR (@ErrorMessage, -- Message text.
@ErrorSeverity, -- Severity.
@ErrorState -- State.
);
END CATCH
GO


Thank you


From: trubar a on
hi

On May 5, 3:19 pm, Erland Sommarskog <esq...(a)sommarskog.se> wrote:
> trubar a (asponmy...(a)gmail.com) writes:
>
> > a) Does the above quote imply that transaction_name parameters are
> > always used just for readability?
>
> Personally, I see little reason to name transaction - unless you use
> the very special WITH MARK
>

So except when used with WITH MARK, transaction_name doesn’t do
anything ( ) but just help in readability

> >The above code is an excerpt from the SQL code I’ve posted at the
> >bottom of this post. In any case, code shown in this excerpt calls
> >COMMIT TRANSACTION only if @TranCounter variable is equal to 0
> >( which means no transaction was started before the procedure was
> >called).
>
>I'm ín even more hurry now, but I'd say the example is hopelessly
>overworked. While you can use savepoints, you don't have to. In my over
>15 years with SQL Server, I think I've used SAVE TRANSACTION twice
>in production code.
>

Uhm, but how else if not via savepoints could you cancel out (via
rollback) only certain commands/changes?


On May 5, 3:57 pm, "Sylvain Lafontaine"
<sylvainlafontaine2...(a)yahoo.ca> wrote:
> Most of your questions have already been answered by Erland, so I just want
> to add a little explanation for the use of @TranCounter in the code example.
> When you call a stored procedure, the transaction count when exiting the SP
> must be the same as it was when calling the SP. This means that if a
> transaction is already open when calling this SP, then you cannot perform a
> rollback inside this SP because the rollback will set the transaction count
> to 0 and therefore, the transaction count will be different when returning
> from the SP and will generate an error.
>

a) Assuming transaction is already opened when we call SP and assuming
that we set a transaction savepoint inside this SP, then we are
allowed to rollback (inside that SP) to that savepoint, but I’m
assuming we’re not allowed to rollback to a savepoint defined outside
this SP?


b) Sorry for being repetitive, but again assuming transaction is
already opened, then trying to call COMMIT inside SP will also set the
transaction count to 0 and cause an error? And for that reason SP also
checked whether @TranCounter equals zero (IF @TranCounter = 0) before
calling COMMIT:

BEGIN TRY
DELETE HumanResources.JobCandidate
WHERE JobCandidateID = @InputCandidateID;
-- Get here if no errors; must commit
-- any transaction started in the
-- procedure, but not commit a transaction
-- started before the transaction was called.
IF @TranCounter = 0
-- @TranCounter = 0 means no transaction was
-- started before the procedure was called.
-- The procedure must commit the transaction
-- it started.
COMMIT TRANSACTION;
END TRY

c) Anyways, again assuming transaction is already opened when we call
SP, why couldn’t/shouldn’t transaction begin outside SP and end inside
SP ( end inside SP either via rollback or commit )?


thanx
From: Erland Sommarskog on
trubar a (asponmynet(a)gmail.com) writes:
> So except when used with WITH MARK, transaction_name doesn�t do
> anything ( ) but just help in readability

....or to add confusion. Although, see below.

>>I'm �n even more hurry now, but I'd say the example is hopelessly
>>overworked. While you can use savepoints, you don't have to. In my over
>>15 years with SQL Server, I think I've used SAVE TRANSACTION twice
>>in production code.
>
> Uhm, but how else if not via savepoints could you cancel out (via
> rollback) only certain commands/changes?

Correct, for that savepoints is the only option. Now: how often do you
really want to do it? OK, *in theory* the answer is always. Say that
you have a procedure A that performs a piece of work, and defines a
user-defined transaction and if the A fails, it rolls back. But what
if A is called from an outer procedure B - and when you wrote A you
did know whether that will happen. If A performs a rollback, it would
pull the rug for B as well. But for B, A may be part of a bigger plot,
and B may want to continue with the rest of its work.

So from this point of view, it would be better if A checked if there
was an active transaction and in such case did a SAVE TRANSACTION
instead of BEGIN TRANSACTION, and in case of ROLLBACK, rolled back
to the savepoint. If you have the same name for the savepoint as
in BEGIN TRANSACION, you only need a single ROLLBACK. (So there is
some use for the name anyway.)

But there are two problems here:
1) The code is complex and error-prone.
2) Lot's of errors in SQL Server causes the transaction to be "doomed";
that is, if there is no TRY-CATCH, the batch and transaction is
aborted directly. If there is a CATCH handler, you can only roll it
back entirely.

So I would say that in SQL Server you would only do it, if you really
need it. As I said, I've used SAVE TRANSACTION twice in production code.
In the first case it was resolve a catch-22 situation. In the second
case it was just like the scenario above. But I had to roll back that
change, beause the code was also called from a linked server - and
you cannot have savepoints in distributed transactions.


> a) Assuming transaction is already opened when we call SP and assuming
> that we set a transaction savepoint inside this SP, then we are
> allowed to rollback (inside that SP) to that savepoint, but I�m
> assuming we�re not allowed to rollback to a savepoint defined outside
> this SP?

That should work. The caller could pass the name of the savepoint in
a variable. (But I have not tested.)

> b) Sorry for being repetitive, but again assuming transaction is
> already opened, then trying to call COMMIT inside SP will also set the
> transaction count to 0 and cause an error? And for that reason SP also
> checked whether @TranCounter equals zero (IF @TranCounter = 0) before
> calling COMMIT:

@TranCounter is the value of @@trancount when the procedure is entered,
so the code is OK. You commit, only if you started the transaction
yourself.

> c) Anyways, again assuming transaction is already opened when we call
> SP, why couldn�t/shouldn�t transaction begin outside SP and end inside
> SP ( end inside SP either via rollback or commit )?

If @@trancount has a different value when the procedure is exited, this
raises error 266, so committed the caller's procedure is not good.
Rolling it back, also produce the error, but there are situations when
you have no other choice.

--
Erland Sommarskog, SQL Server MVP, esquel(a)sommarskog.se

Links for SQL Server Books Online:
SQL 2008: http://msdn.microsoft.com/en-us/sqlserver/cc514207.aspx
SQL 2005: http://msdn.microsoft.com/en-us/sqlserver/bb895970.aspx
SQL 2000: http://www.microsoft.com/sql/prodinfo/previousversions/books.mspx