IT tutorials
 
Database
 

SQL Server 2008 R2 : Transactions and Stored Procedures

5/29/2013 7:42:05 PM
- Free product key for windows 10
- Free Product Key for Microsoft office 365
- Malwarebytes Premium 3.7.1 Serial Keys (LifeTime) 2019

Because SQL code in stored procedures runs locally on the server, it is recommended that entire transactions be completely encapsulated within stored procedures to speed transaction processing. This way, the entire transaction executes within a single stored procedure call from the client application, rather than being executed across multiple requests. The less network traffic that occurs between the client application and SQL Server during transactions, the faster they can finish.

Another advantage of using stored procedures for transactions is that doing so helps you avoid the occurrence of partial transactions—that is, transactions that are started but not fully committed. Using stored procedures this way also avoids the possibility of user interaction within a transaction. The stored procedure keeps the transaction processing completely contained because it starts the transaction, carries out the data modifications, completes the transaction, and returns the status or data to the client.

Stored procedures also provide the additional benefit that if you need to fix, fine-tune, or expand the duties of the transaction, you can do all this at one time, in one central location. Your applications can share the same stored procedure, providing consistency for the logical unit of work across your applications.

Although stored procedures provide a useful solution to managing transactions, you need to know how transactions work within stored procedures and code for them appropriately. Consider what happens when one stored procedure calls another, and they both do their own transaction management. Obviously, they now need to work in concert with each other. If the called stored procedure has to roll back its work, how can it do so correctly without causing data integrity problems?

The issues you need to deal with go back to the earlier topics of transaction nesting and transaction flow versus program flow. Unlike a rollback in a trigger (see the next section), a rollback in a stored procedure does not abort the rest of the batch or the calling procedure.

For each BEGIN TRAN encountered in a nested procedure, the transaction nesting level is incremented by 1. For each COMMIT encountered, the transaction nesting level is decremented by 1. However, if a rollback other than to a named savepoint occurs in a nested procedure, it rolls back all statements to the outermost BEGIN TRAN, including any work performed inside the nested stored procedures that has not been fully committed. It then continues processing the remaining commands in the current procedure as well as the calling procedure(s).

To explore the issues involved, you can work with the sample stored procedure shown in Listing 1. The procedure takes a single integer argument, which it then attempts to insert into a table (test_table). All data entry attempts—whether successful or not—are logged to a second table (auditlog). Listing 1 contains the code for the stored procedure and the tables it uses.

Listing 1. Sample Stored Procedure and Tables for Transaction Testing
CREATE TABLE test_table (col1 int)
go
CREATE TABLE auditlog (who varchar(128), valuentered int null)
go
CREATE PROCEDURE trantest @arg INT
AS
BEGIN TRAN
   IF EXISTS( SELECT * FROM test_table WHERE col1 = @arg )
   BEGIN
      RAISERROR ('Value %d already exists!', 16, -1, @arg)
      ROLLBACK TRANSACTION
   END
   ELSE
   BEGIN
      INSERT INTO test_table (col1) VALUES (@arg)
      COMMIT TRAN
   END

INSERT INTO auditlog (who, valuentered) VALUES (USER_NAME(), @arg)
 return

Now explore what happens if you call this stored procedure in the following way and check the values of the two tables:

set nocount on
EXEC trantest 1
EXEC trantest 2
SELECT * FROM test_table
SELECT valuentered FROM auditlog
go

The execution of this code gives the following results:

col1
-----------
1
2

valuentered
-----------
1
2

These would be the results you would expect because no errors would occur, and nothing would be rolled back.

Now, if you were to run the same code a second time, test_table would still have only two rows because the procedure would roll back the attempted insert of the duplicate rows. However, because the procedure and batch are not aborted, the code would continue processing, and the rows would still be added to the auditlog table. The result would be as follows:

set nocount on
EXEC trantest 1
EXEC trantest 2
SELECT * FROM test_table
SELECT valuentered FROM auditlog
go

Msg 50000, Level 16, State 1, Procedure trantest, Line 6
Value 1 already exists!

Msg 50000, Level 16, State 1, Procedure trantest, Line 6
Value 2 already exists!

col1
-----------
1
2


valuentered
-----------
1
2
1
2

Now explore what happens when you execute the stored procedure from within a transaction:

set nocount on
BEGIN TRAN
EXEC trantest 3
EXEC trantest 1
EXEC trantest 4
COMMIT TRAN
SELECT * FROM test_table
SELECT valuentered FROM auditlog
go

The execution of this code gives the following results:

Msg 50000, Level 16, State 1, Procedure trantest, Line 6
Value 1 already exists!

Msg 266, Level 16, State 2, Procedure trantest, Line 0
Transaction count after EXECUTE indicates that a COMMIT or ROLLBACK TRANSACTION
 statement is missing. Previous count = 1, current count = 0.

Msg 3902, Level 16, State 1, Line 6
The COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION.

col1
-----------
1
2
4

valuentered
-----------
1
2
1
2
1
4


					  

A number of problems are occurring now. For starters, you get a message telling you that the transaction nesting level was messed up. More seriously, the results show that the value 4 made it into the test_table table anyway and that the auditlog table picked up the inserts of 1 and the 4 but lost the fact that you tried to insert a value of 3. What happened?

Let’s examine this example one step at a time. First, you start the transaction and insert the value 3 into trantest. The stored procedure starts its own transaction, adds the value to test_table, commits that, and then adds a row to auditlog. Next, you execute the procedure with the value 1. This value already exists in the table, so the procedure raises an error and rolls back the transaction. Remember that a ROLLBACK undoes work to the outermost BEGIN TRAN—which means the start of this batch. This rolls back everything, including the insert of 3 into trantest and auditlog. The auditlog entry for the value 1 is inserted and not rolled back because it occurred after the transaction was rolled back and is a standalone, automatically committed statement now.

You then receive an error regarding the change in the transaction nesting level because a transaction should leave the state of a governing procedure in the same way it was entered; it should make no net change to the transaction nesting level. In other words, the value of @@trancount should be the same when the procedure exits as when it was entered. If it is not, the transaction control statements are not properly balanced.

Also, because the batch is not aborted, the value 4 is inserted into trantest, an operation that completes successfully and is automatically committed. Finally, when you try to commit the transaction, you receive the last error regarding a mismatch between BEGIN TRAN and COMMIT TRAN because no transaction is currently in operation.

The solution to this problem is to write the stored procedures so that transaction nesting doesn’t occur and so the stored procedure rolls back only its own work. When a rollback occurs, it should return an error status so that the calling batch or procedure is aware of the error condition and can choose to continue or abort the work at that level. You can manage this by checking the current value of @@trancount and determining what needs to be done. If a transaction is already active, the stored procedure should not issue a BEGIN TRAN and nest the transaction; rather, it should set a savepoint. This allows the procedure to perform a partial rollback of its work. If no transaction is active, the procedure can safely begin a new transaction. The following SQL code fragment is an example of using this approach:

DECLARE @trancount INT
/* Capture the value of the transaction nesting level at the start */
SELECT @trancount = @@trancount
IF (@trancount = 0)  — no transaction is currently active, start one
   BEGIN TRAN mytran
ELSE                 — a transaction is active, set a savepoint only
   SAVE TRAN mytran
.
.
/* This is how to trap an error. Roll back either to your
   own BEGIN TRAN or roll back to the savepoint. Return an
   error code to the caller to indicate an internal failure.
   How the caller handles the transaction is up to the caller.*/
IF (@@error <> 0)
BEGIN
   ROLLBACK TRAN mytran
   RETURN –1969
END
.
.
/* Once you reach the end of the code, you need to pair the BEGIN TRAN,
   if you issued it, with a COMMIT TRAN. If you executed the SAVE TRAN
   instead, you have nothing else to do...end of game! */
IF (@trancount = 0)
  COMMIT TRAN

RETURN 0


					  

If you apply these concepts to all stored procedures that need to incorporate transaction processing as well as the code that calls the stored procedures, you should be able to avoid problems with transaction nesting and inconsistency in your transaction processing. You just need to be sure to check the return value of the stored procedure and determine whether the whole batch should be failed or whether that one call is of little importance to the overall outcome and the transaction can continue.

 
Others
 
 
 
Top 10
 
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 2) - Wireframes,Legends
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 1) - Swimlanes
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Formatting and sizing lists
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Adding shapes to lists
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Sizing containers
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 3) - The Other Properties of a Control
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 2) - The Data Properties of a Control
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 1) - The Format Properties of a Control
- Microsoft Access 2010 : Form Properties and Why Should You Use Them - Working with the Properties Window
- Microsoft Visio 2013 : Using the Organization Chart Wizard with new data
Technology FAQ
- Is possible to just to use a wireless router to extend wireless access to wireless access points?
- Ruby - Insert Struct to MySql
- how to find my Symantec pcAnywhere serial number
- About direct X / Open GL issue
- How to determine eclipse version?
- What SAN cert Exchange 2010 for UM, OA?
- How do I populate a SQL Express table from Excel file?
- code for express check out with Paypal.
- Problem with Templated User Control
- ShellExecute SW_HIDE
programming4us programming4us