MS SQL Server
  Home arrow MS SQL Server arrow Page 5 - Scripts and Transactions in Transact-SQL
ASP Free Forums 
.NET  
ASP  
ASP Code  
ASP.NET  
ASP.NET Code  
BrainDump  
C#  
Code Examples  
Database  
Database Code  
IIS  
Microsoft Access  
MS SQL Server  
Visual Basic.NET  
Windows Scripting  
Windows Security  
XML  
ASP Web Hosting  
ASP.NET Web Hosting 
Mobile Linux 
App Generation ROI 
Windows Web Hosting
 
IBM® developerWorks 
Sun Developer Network 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
 RSS  Articles
 RSS  Forums
 RSS  All Feeds
Write For Us Get Paid 
Request Media Kit
Contact Us 
Site Map 
Privacy Policy 
Support 
 USERNAME
 
 PASSWORD
 
 
  >>> SIGN UP!  
  Lost Password? 
MS SQL SERVER

Scripts and Transactions in Transact-SQL
By: McGraw-Hill/Osborne
  • Search For More Articles!
  • Disclaimer
  • Author Terms
  • Rating: 4 stars4 stars4 stars4 stars4 stars / 4
    2006-08-17

    Table of Contents:
  • Scripts and Transactions in Transact-SQL
  • Transactions
  • Explicit and Implicit Transactions
  • Transaction Processing Architecture
  • Nested Transactions

  • Rate this Article: Poor Best 
      ADD THIS ARTICLE TO:
      Del.ici.ous Digg
      Blink Simpy
      Google Spurl
      Y! MyWeb Furl
    Email Me Similar Content When Posted
    Add Developer Shed Article Feed To Your Site
    Email Article To Friend
    Print Version Of Article
    PDF Version Of Article
     
     
    ADVERTISEMENT


    Scripts and Transactions in Transact-SQL - Nested Transactions


    (Page 5 of 5 )

    SQL Server allows you to nest transactions. Basically, this feature means that a new transaction can start even though the previous one is not complete:

    Begin transaction
    ...
        Begin transaction
        ...
        Commit transaction
    ...
    Commit transaction

    Usually this situation occurs when one stored procedure containing a transaction calls another stored procedure that also contains a transaction. In the following example, ap_Order_Complete_1 completes an order by setting its completion date and changing the status of the order, and then looping through associated order items and calling ap_OrderItem_Complete_1 to complete each of them. ap_OrderItem_Complete_1 sets the completion date of an order item to the last ChargeLog date associated with that OrderItem. Both of these procedures contain a transaction.

    Create Procedure ap_Order_Complete_1
    -- Complete all orderItems and then complete order
        @intOrderId int,
        @dtsCompletionDate smalldatetime
    As
    set nocount on

    Declare @intErrorCode int,
            @i int,
            @intCountOrderItems int,
            @intOrderItemId int
    Select @intErrorCode = @@Error

    If @intErrorCode = 0
        Begin Transaction

    -- complete order
    If @intErrorCode = 0
    Begin
         Update dbo.[Order]
         Set CompletionDate = @dtsCompletionDate,
             OrderStatusId = 4 -- completed
         Where OrderId = @intOrderId

         Select @intErrorCode = @@Error
    End

    -- loop through OrderItems and complete them
    If @intErrorCode = 0
    Begin
         Create Table #OrderItems(
              id int identity(1,1),
              OrderItemId int)

         Select @intErrorCode = @@Error
    End

    -- collect orderItemIds
    If @intErrorCode = 0
    Begin
         Insert Into #OrderItems(OrderItemId)
              Select ItemId
              From dbo.OrderItem
              Where OrderId = @intOrderId  
              Select @intErrorCode = @@Error End

    If @intErrorCode = 0
    Begin
         Select @intCountOrderItems = Max(Id),
                @i = 1
         From #OrderItems

         Select @intErrorCode = @@Error
    End

    while @intErrorCode = 0 and @i <= @intCountOrderItems
    Begin
         If @intErrorCode = 0
         Begin
              Select @intOrderItemId = OrderItemId
              From #OrderItems
              Where id = @i
              Select @intErrorCode = @@Error  
         End

         If @intErrorCode = 0
              Exec @intErrorCode = dbo.ap_OrderItem_Complete_1 @intOrderItemId

        If @intErrorCode = 0
              Set @i = @i + 1
    End

    If @intErrorCode = 0 and @@trancount > 0
          Commit Transaction
    Else
          Rollback Transaction
    return @intErrorCode
    Go

    create Procedure dbo.ap_OrderItem_Complete_1
    -- Set CompletionDate of OrderItem to date -- of last ChargeLog record associated with OrderItem.
         @intOrderItemId int
    As
    set nocount on
    Declare @intErrorCode int
    Select @intErrorCode = @@Error

    If @intErrorCode = 0
         Begin Transaction

    -- Set CompletionDate of OrderItem to date -- of last ChargeLog record associated with OrderItem.
    If @intErrorCode = 0
    Begin
         update dbo.OrderItem
         Set CompletionDate = (Select Max(ChargeDate)
                               from dbo.ChargeLog
                               where ItemId = @intOrderItemId)
        Where ItemId = @intOrderItemId

        Select @intErrorCode = @@Error
    End

    If @intErrorCode = 0
    Begin
         exec @intErrorCode = dbo.ap_NotifyAccounting @intOrderItemId
    End

    If @intErrorCode = 0 and @@trancount > 0
         Commit Transaction
    Else

          Rollback Transaction
    Return @intErrorCode

    In the case of nested transactions, no Commit statements except the outer one will save changes to the database. Only after the last transaction is committed will all changes to the database become permanent. Up to that point, it is still possible to roll back all changes.

    The interesting question is how SQL Server knows which transaction is the last one. It keeps the number of opened transactions in the @@trancount global variable for each user connection. When SQL Server encounters a Begin Transaction statement, it increments the value of the @@trancount, and when SQL Server encounters a Commit Transaction statement, it decrements the value of the @@trancount. Therefore, the only effect of a nested (internal) Commit Transaction statement is a change to the @@trancount value. Only the outer Commit Transaction statement (when @@trancount = 1) stores changes in data tables rather than in the transaction log.

    The following is a purely academic example that does not perform any real processing, but it demonstrates the effect of nested transactions on the @@trancount global variable:

    print 'Trancount = ' + Convert(varchar(4), @@trancount)
    BEGIN TRANSACTION
       
    print 'Trancount = ' + Convert(varchar(4), @@trancount)
        BEGIN TRANSACTION
       
    print 'Trancount = ' + Convert(varchar(4), @@trancount)
        COMMIT TRANSACTION
       
    print 'Trancount = ' + Convert(varchar(4), @@trancount)
    COMMIT TRANSACTION
    print 'Trancount = ' + Convert(varchar(4), @@trancount)

    Each transactional statement will increment and decrement the @@trancount:

    Trancount = 0
    Trancount = 1
    Trancount = 2
    Trancount = 1
    Trancount = 0

    An interesting inconsistency to observe is in the behavior of the Rollback Transaction statement. No matter how many transaction levels deep execution extends, the Rollback Transaction statement will cancel all changes caused by all transactions (and bring the @@trancount value down to zero). In fact, if you execute an additional Rollback Transaction statement after the first one, SQL Server will report an error.

    print 'Trancount = ' + Convert(varchar(4), @@trancount)
    BEGIN TRANSACTION
       
    print 'Trancount = ' + Convert(varchar(4), @@trancount)
        BEGIN TRANSACTION
       
    print 'Trancount = ' + Convert(varchar(4), @@trancount)
        ROLLBACK TRANSACTION
       
    print 'Trancount = ' + Convert(varchar(4), @@trancount)
    ROLLBACK TRANSACTION
    print 'Trancount = ' + Convert(varchar(4), @@trancount)

    The following is the result of this example:

    Trancount = 0
    Trancount = 1
    Trancount = 2
    Trancount = 0
    Server: Msg 3903, Level 16, State 1, Line 8 The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION.
    Trancount = 0

    TIP


    I have to admit that I had many problems with this issue at one time. Be careful.

    To prevent this error, you need to test for the value of the @@trancount variable before you execute the Rollback Transaction statement. A simple way to test for this value works something like this:

    if @@trancount > 0
        Rollback Transaction

    You will find a much better solution in Chapter 6.

    Please check back next week for the conclusion of this article.


    DISCLAIMER: The content provided in this article is not warranted or guaranteed by Developer Shed, Inc. The content provided is intended for entertainment and/or educational purposes in order to introduce to the reader key ideas, concepts, and/or product reviews. As such it is incumbent upon the reader to employ real-world tactics for security and implementation of best practices. We are not liable for any negative consequences that may result from implementing any information covered in our articles or tutorials. If this is a hardware review, it is not recommended to open and/or modify your hardware.

       · This article is an excerpt from the book "Microsoft SQL Server 2005 Stored Procedure...
     

    Buy this book now. This article is excerpted from chapter five of the book Microsoft SQL Server 2005 Stored Procedure Programming in T-SQL and .NET, written by Dejan Sunderic (McGraw-Hill/Osborne, 2006; ISBN: 0072262281). Buy this book now.

    MS SQL SERVER ARTICLES

    - Completing the Introduction to Transact-SQL
    - A Brief Introduction to Transact-SQL
    - Lookups and Blocking Bad Data
    - Field Validation Rules for Blocking Bad Data
    - Using Masks to Block Bad Data
    - Blocking Bad Data
    - Using @@ROWCOUNT and TABLE Variables for Dat...
    - How to Use Variables, IF and CASE in Databas...
    - Creating Important Aspects of Notification S...
    - Working wth Variables in Database Interactio...
    - Delving Deeper into Notification Services
    - Notification Services
    - Building a Multi-table Report with SQL 2005 ...
    - A Secure Way of Building Connection Strings
    - Transferring a Database Using the SSIS Desig...





    © 2003-2008 by Developer Shed. All rights reserved. DS Cluster 3 hosted by Hostway
    Stay green...Green IT