历史上,SQL中的错误处理仅限于在致命错误后的事务回滚。然而,随着业务逻辑的发展,越来越需要准确的错误日志记录,仅回滚已执行的更改,以及能够支持存储过程的嵌套调用(nested transactions)。
问题在于,并非所有的数据库管理系统(DBMS)都完全支持嵌套事务(例如,在MS SQL Server中,这更像是保存点(SAVEPOINT)的级别,而不是真正的事务),而在错误发生时,如果没有使用TRY/CATCH或类似机制,异常可能会提前中断执行,导致必要的清理未完成。
解决方案:使用TRY/CATCH(或类似结构)、SAVEPOINT以支持嵌套,以及配置过程在出错时的行为(例如,SQL Server中的SET XACT_ABORT)。
代码示例(MS SQL Server):
BEGIN TRY BEGIN TRANSACTION -- 操作 EXEC dbo.InnerProcedure COMMIT TRANSACTION END TRY BEGIN CATCH IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION -- 错误日志记录 INSERT INTO error_log VALUES (ERROR_NUMBER(), ERROR_MESSAGE(), GETDATE()) END CATCH
关键特点:
在嵌套事务中发生错误时,所有更改是否真实回滚?
在大多数DBMS中,嵌套事务并不是独立的事务,而是回退点(SAVEPOINT)。在整体回滚时,将回滚所有更改,直到最初的(外部)事务。
如果忘记检查@@TRANCOUNT并在没有活动事务的情况下执行ROLLBACK,会发生什么?
会引发错误——没有活动事务可回滚。在执行ROLLBACK之前,始终检查@@TRANCOUNT > 0。
TRY/CATCH中会执行触发器和游标吗?
是的,但触发器中的错误可能会导致转到CATCH并回滚整个事务。需要在过程体以及被调用对象(过程、触发器)中考虑错误处理。
几个嵌套的过程,每个过程都使用BEGIN TRANSACTION / COMMIT而没有TRY/CATCH,发生错误时部分数据已更改,部分未更改。
优点:
缺点:
所有逻辑都封装在TRY/CATCH中,嵌套过程使用SAVEPOINT或在一个外部事务内操作,所有错误均记录。
优点:
缺点: