事务中的TSQL Try / Catch,反之亦然?

时间:2014-04-14 09:50:10

标签: sql-server tsql transactions sql-delete

我正在编写一个脚本来删除多个表中的记录,但在删除之前,它必须返回一个计数供用户在提交之前进行确认。

这是该剧本的摘要。

BEGIN TRANSACTION SCHEDULEDELETE
    BEGIN TRY
        DELETE   -- delete commands full SQL cut out
        DELETE   -- delete commands full SQL cut out
        DELETE   -- delete commands full SQL cut out
        PRINT 'X rows deleted. Please commit or rollback.' --calculation cut out.
    END TRY
    BEGIN CATCH 
        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

            ROLLBACK TRANSACTION SCHEDULEDELETE
            PRINT 'Error detected, all changes reversed.'
    END CATCH

--COMMIT TRANSACTION SCHEDULEDELETE --Run this if count correct.

--ROLLBACK TRANSACTION SCHEDULEDELETE --Run this if there is any doubt whatsoever.

这是我第一次编写事务,在事务中使用TRY / CATCH块或者事务是否在TRY块内是正确/最佳实践吗?

此脚本中的重要因素是用户必须手动提交事务。

3 个答案:

答案 0 :(得分:40)

只有在TRY块内并且在实际语句之前,才会打开事务,并立即提交。不要等待控件转到批处理结束以提交事务。

如果在TRY块中出现问题并且您已打开事务,则控件将跳转到CATCH块。只需在那里回滚您的交易,并根据需要进行其他错误处理。

在实际回滚事务之前,我已使用@@TRANCOUNT函数为任何打开的事务添加了一点检查。在这种情况下,它并没有多大意义。在打开事务之前,在TRY块中进行一些验证检查更有用,例如检查参数值和其他内容,如果任何验证检查失败,则会在TRY块中引发错误。在这种情况下,控件将跳转到CATCH块,甚至不会打开事务。在那里你可以检查任何打开的交易和回滚,如果有任何打开的。在您的情况下,您实际上不需要检查任何打开的事务,因为除非您的事务中出现问题,否则您将不会进入CATCH块。

执行DELETE操作后,不要询问是否需要提交或回滚;在打开交易之前进行所有这些验证。一旦打开一个事务,立即提交它,如果有任何错误,请进行错误处理(通过使用几乎所有错误函数获取详细信息,你做得很好)。

BEGIN TRY

  BEGIN TRANSACTION SCHEDULEDELETE
    DELETE   -- delete commands full SQL cut out
    DELETE   -- delete commands full SQL cut out
    DELETE   -- delete commands full SQL cut out
 COMMIT TRANSACTION SCHEDULEDELETE
    PRINT 'X rows deleted. Operation Successful Tara.' --calculation cut out.
END TRY

BEGIN CATCH 
  IF (@@TRANCOUNT > 0)
   BEGIN
      ROLLBACK TRANSACTION SCHEDULEDELETE
      PRINT 'Error detected, all changes reversed'
   END 
    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

答案 1 :(得分:3)

除了上面M.Ali和院长的好建议之外,对于那些希望在SQL SERVER中使用新的(呃)TRY CATCH THROW范例的人来说有点帮助:

(我无法轻易找到完整的语法,因此请在此处添加)

GIST:HERE

此处的示例存储过程代码(来自我的要点):

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

CREATE PROC [dbo].[pr_ins_test]
@CompanyID INT
AS

SET NOCOUNT ON

BEGIN

    DECLARE @PreviousConfigID INT

    BEGIN TRY
        BEGIN TRANSACTION MYTRAN; -- Give the transaction a name
        SELECT 1/0  -- Generates divide by zero error causing control to jump into catch

        PRINT '>> COMMITING'
        COMMIT TRANSACTION MYTRAN;
    END TRY
    BEGIN CATCH
        IF @@TRANCOUNT > 0
        BEGIN 
            PRINT '>> ROLLING BACK'
            ROLLBACK TRANSACTION MYTRAN; -- The semi-colon is required (at least in SQL 2012)


        END
        THROW
    END CATCH
END

答案 2 :(得分:2)

永远不要等待最终用户提交事务,除非它是单用户模式数据库。

简而言之,它是关于阻止的。您的事务将对正在更新的资源进行一些独占锁定,并将保留这些锁定,直到事务结束(已提交或回滚)。没有人能够触摸这些行。如果快照隔离与版本存储清理一起使用,则存在一些不同的问题。

最好首先发出一个选择查询以确定多个符合条件的行,将其呈现给最终用户,并在确认后进行实际删除。