事务与@@TranCount

      数据库引擎并不只用于数据存储,其核心功能之一是在允许最大数量的并发访问时保证数据的一致性。如果SQL Server数据库中的数据一直不变,则数据的一致性的问题也就不会发生。但其他用户试图访问某数据时,一个用户很可能正在更改此数据。

      每次当一个用户修改某条数据时,他可以选择是保存修改(COMMIT)还是放弃修改(ROLLBACK)。SQL Server必须保证用户在读数据时获取的是已提交(COMMITED)到数据库的数据,从而确保结果的一致。

      事务是组成一个逻辑工作单元的一组数据库操作。每个事务都被界定为隐式的或是显式的。每个事务以BEGIN TRAN 开头,接下来是一条或多条待执行的命令。由COMMIT TRAN或ROLLBACK TRAN 来中断事务。隐式事务只发生于执行单条命令时,该命令可以是INSERT,UPDATE,或DELETE,这些命令以BEGIN TRAN 开头,以COMMIT TRAN 结束。例如,迄今为止我们在本书的学习过程中所提交的批处理都是隐式事务。

      BEGIN TRAN 将开始一个新事务,BEGIN TRAN保存最里层的事务,ROLLBACK TRAN 将回滚当前连接的所有事务,撤销已做的更改,并释放所有锁定。

  BEGIN TRAN 和COMMIT TRAN 描述了一个事务。SQL Server 锁定数据,以保证应用获取一致数据。锁是多用户环境中对资源的访问限制,在事务开始时应用,当事务提交或回滚时释放。SQL Server 中应用的锁有如下几种类型:

Shared(共享)   Exclusive(排他)  Update(更新)

  在处理事务的时候,一般都用RollBack Transaction 来回滚,但是如果在嵌套事务中这样使用的话就会出现错误。

  在Sql Server里,嵌套事务的层次是由@@TranCount全局变量反映出来的。每一次Begin Transaction(事务) 都会引起@@TrananCount加1.而每一次Commit  Transaction 都会使@@TranCount 减1,而RollBack Transaction 会回滚所有的嵌套事务包括已经提交的事务和未提交的事务,而使@@TranCount置0.

   如果被嵌套的事务中发生错误,最简单的方法应该是无论如何都先将它提交,同时返回错误码(一个正常情况不可能出现的代码 如 -1)让上一层事务来处理这个错误,从而使@@TranCount 减1。 这样外层事务在回滚或者提交的时候能够保证外层事务在开始的时候和结束的时候保持一致。由于里层事务返回了错误码,因此外层事务(最外层)可以回滚事务,这样里面已经提交的事务也可以被回滚而不会出现错误。

      在项目中应该会常常出现这样的情况,一个存储过程里面用了事务,但是不能保证它会被别的带有事务的存储过程调用,如果单独调用的话,出现错误可以直接回滚,但是如果是被别的带事务的存储过程调用的话,RollBack 就会出错了。因此需要一种机制来区分,建立一个临时的变量来区分是否嵌套,和嵌套的层数,如下:

IF EXISTS (SELECT name FROM sys.objects
WHERE name = N'SaveTranExample')
DROP PROCEDURE SaveTranExample;
GO
CREATE PROCEDURE SaveTranExample
@InputCandidateID INT
AS
-- Detect whether 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

SAVE TRANSACTION (Transact-SQL)

在事务内设置保存点。

语法

 SAVE { TRAN | TRANSACTION } { savepoint_name | @savepoint_variable }  
[ ; ]  

参数
 savepoint_name

分配给保存点的名称。 保存点名称必须符合标识符的规则,但长度不能超过 32 个字符。 transaction_name 始终是区分大小写的,即使 SQL Server 实例不区分大小写也是如此。

@savepoint_variable
包含有效保存点名称的用户定义变量的名称。 必须用 char、varchar、nchar 或 nvarchar 数据类型声明变量。 如果长度超过 32 个字符,也可以传递到变量,但只使用前 32 个字符。

备注
用户可以在事务内设置保存点或标记。 保存点可以定义在按条件取消某个事务的一部分后,该事务可以返回的一个位置。 如果将事务回滚到保存点,则根据需要必须完成其他剩余的 Transact-SQL 语句和 COMMIT TRANSACTION 语句,或者必须通过将事务回滚到起始点完全取消事务。 若要取消整个事务,请使用 ROLLBACK TRANSACTION transaction_name 语句。 这将撤消事务的所有语句和过程。

在事务中允许有重复的保存点名称,但指定保存点名称的 ROLLBACK TRANSACTION 语句只将事务回滚到使用该名称的最近的 SAVE TRANSACTION。

在使用 BEGIN DISTRIBUTED TRANSACTION 显式启动或从本地事务升级的分布式事务中,不支持 SAVE TRANSACTION。

要求具有 public 角色的成员身份。
 
 

  根据所执行的操作类型分配不同的锁类型。可以每次只想一个资源分配一种锁。例如当在某个数据行上拥有了一个共享锁,则不能再向该行分配其他的锁。

  共享锁为select 语句而分配。因为select 语句不能修改任何数据,因此可以在任何时候向单个资源上分配多个共享锁。共享锁将阻止获取排它锁。

  执行insert 或delete 语句时,资源将获取排它锁。直至事务完成前,排它锁将阻止任何试图访问被锁定资源的连接。由其定义不难看出,任何时候,针对一个资源只能设置一个排它锁。

  更新锁其实是共享锁和排它锁的特例。SQL Server 需要在表中定位数据以执行update 语句。当SQL Server 定位表中需修改的数据时,update语句获取更新锁以开始执行,一旦完成全部受影响的行的定位时,共享锁变为排它锁。

  锁可以应用于三种不同的层级:  行   页   表

  SQL Server 根据最小化维护锁所需资源的要求,自动管理锁的层级。例如在需要访问某页上50%的数据时,对于SQL    Server 而言,在页上放置单个锁所需资源,要比在多行上放置几个甚至上几百个锁少的多。

  SQL Server 根据优化器锁提供的数据对锁层级加以学习,从而可进行一定的猜测。然而我们不能期望第一次的猜测便十分精确,优化器收集的每一类查询的数据也并不总是确切的。因此SQL Server提供了一种所谓“锁升级”的机制以改进锁的层级。锁升级可以将行级的锁提升为页级,也可以将页级的锁提升为表级。

  事务处理中最后一处难以理解的地方便是隔离级别(isolation level)。隔离级别决定了事务处理期间将放置何种锁以及锁将在何时被释放。

  SQL Server 2008中有五种隔离级别:

  Read Uncommitted(未提交读)

  Read Committed(已提交读)

  Repeatable read(可重复读)

  Serializable(可序列化)

  Snapshot(快照)

     Snapshot 隔离级别允许您读取正在被修改的行,也允许修改正在被读取的行。当然,如果未被提交,您将获取不到任何数据。快照隔离级别确保您只收到已提交的数据,而且通过在存储引擎中权衡行版本功能以确保不被阻塞。当您试图读取一条正被写的数据时,SQL Server 将向您返回修改之前的行版本。

 

posted @ 2016-07-13 17:16  云~~~  阅读(484)  评论(0编辑  收藏  举报