日期:2014-05-17  浏览次数:20426 次

存储过程中事务回滚的问题
有两张表,分别为Table1(结构:COLA1 nvarchar(10) null,COLA2 nvarchar(10) null) 和Table2(结构:COLB1 nvarchar(10) not null,COLB2 nvarchar(10) null),向两张表中用INSERT INTO写入数据,用事务处理,如果有一张表写入不成功,则两张表的数据都不写入。
我写了一个存储过程:
[code=SQL][/code]
CREATE PROCEDURE Sp_CU_Test(
  @AA1 nvarchar(10),
  @AA2 nvarchar(10),
  @BB1 nvarchar(10),
  @BB2 nvarchar(10)
)
AS
DECLARE @cSqlString1 nvarchar(4000)
DECLARE @cSqlString2 nvarchar(4000)
BEGIN TRAN
SET @cSqlString1 ='INSERT INTO Table1(COLA1,COLA2) VALUES('''+@AA1+''' ,'''+@AA2+''')'
SET @cSqlString2 ='INSERT INTO Table2(COLB1,COLB2) VALUES('''+@BB1+''' ,'''+@BB2+''')'
exec(@cSqlString1+' '+ @cSqlstring2)
if @@ERROR <>0
  begin
  rollback tran
  end
else
  commit tran  
Go  

结果:测试 EXEC Sp_CU_Test 'AA1','AA1',NULL,'BB1'
  结果:Table1中写入了记录,Table2中没有写入记录。(要求是如果有一张表写入数据不成功则回滚,而测试结果是Table1写入了记录,Table2没有写入记录)

另外做了一个测试的存储过程:
CREATE PROCEDURE Sp_CU_Test(
  @AA1 nvarchar(10),
  @AA2 nvarchar(10),
  @BB1 nvarchar(10),
  @BB2 nvarchar(10)
)
AS
DECLARE @cSqlString1 nvarchar(4000)
DECLARE @cSqlString2 nvarchar(4000)
BEGIN TRAN
--SET @cSqlString1 ='INSERT INTO Table1(COLA1,COLA2) VALUES('''+@AA1+''' ,'''+@AA2+''')'
--SET @cSqlString2 ='INSERT INTO Table2(COLB1,COLB2) VALUES('''+@BB1+''' ,'''+@BB2+''')'
--exec(@cSqlString1+' '+ @cSqlstring2)
INSERT INTO TABLE1(COLA1,COLA2) VALUES(@AA1,@AA2 )
INSERT INTO TABLE2(COLB1,COLB2) VALUES(@BB1,@BB2 )

if @@ERROR <>0
  begin
  rollback tran
  end
else
  commit tran  
Go  

测试结果:EXEC Sp_CU_Test 'AA1','AA2',NULL,'BB1'
  在Table1和Table2中都没有写入数据(写Table1的语句可以写入记录,写Table2的语句由于COLB1的值是不允许为NULL,所以不可以写入记录,最后回滚以后,两张表都记录)

问题:1、这两个存储过程看上去差不多,为什么结果会不一样呢?
  2、第一个存储过程要怎么样修改才能实现第二个存储过程那样的效果?

------解决方案--------------------
动态语句如下实现

SQL code
CREATE PROCEDURE Sp_CU_Test(
  @AA1 nvarchar(10),
  @AA2 nvarchar(10),
  @BB1 nvarchar(10),
  @BB2 nvarchar(10)
)
AS
DECLARE @cSqlString1 nvarchar(4000)
DECLARE @cSqlString2 nvarchar(4000)
SET @cSqlString1 ='INSERT INTO Table1(COLA1,COLA2) VALUES('''+@AA1+''' ,'''+@AA2+''')'
SET @cSqlString2 ='INSERT INTO Table2(COLB1,COLB2) VALUES('''+@BB1+''' ,'''+@BB2+''')'
DECLARE @SQL NVARCHAR(400)
SET @SQL = N'
SET XACT_ABORT ON
BEGIN TRAN
' + @cSqlString1 + '
' + @cSqlString2 + '
commit tran
'
exec(@SQL)
Go

------解决方案--------------------
SQL code
SET @cSqlString1 ='INSERT INTO Table1(COLA1,COLA2) VALUES('''+@AA1+''' ,'''+@AA2+''')'
SET @cSqlString2 ='INSERT INTO Table2(COLB1,COLB2) VALUES('''+@BB1+''' ,'''+@BB2+''')'
DECLARE @SQL NVARCHAR(8000)

--2005/2008
BEGIN TRY
BEGIN TRAN
    exec(@cSqlString1+' '+ @cSqlstring2)
    IF (XACT_STATE()) = 1
    COMMIT TRAN
END TRY
BEGIN CATCH
    IF (XACT_STATE()) != 1
    ROLLBACK TRAN
    SELECT
        ERROR_NUMBER() AS ErrorNumber
        ,ERROR_SEVERITY() AS ErrorSeverity
        ,ERROR_STATE() AS ErrorState
        ,ERROR_PROCEDURE() AS ErrorProcedure
        ,ERROR_LINE() AS ErrorLine
        ,ERROR_MESSAGE() AS ErrorMessage;

END CATCH

------解决方案--------------------
SQL code

CREATE PROCEDURE Sp_CU_Test
( @AA1 nvarchar(10),
  @AA2 nvarchar(10),
  @BB1 nvarchar(10),
  @BB2 nvarchar(10))
AS
DECLARE @cSqlString1 nvarchar(4000)
DECLARE @cSqlString2 nvarchar(4000)
SET @cSqlString1 ='INSERT INTO Table1(COLA1,COLA2) VALUES('''+@AA1+''' ,'''+@AA2+''')'
SET @cSqlString2 ='INSERT INTO Table2(COLB1,COLB2) VALUES('''+@BB1+''' ,'''+@BB2+''')'

set xact_abort on