首先,您应该在所有过程中始终进行适当的事务处理,以便由应用程序代码,其他过程,在临时查询中单独进行,由SQL Agent作业或其他方式调用它们都无所谓。但是,单个DML语句或未进行任何修改的代码不需要显式的Transaction。所以,我推荐的是:
- 始终具有TRY / CATCH结构,以便可以正确冒起错误
- 如果您有多个DML语句,则可以选择在下面的代码中包含3个事务处理部分(因为单个语句本身就是事务)。但是,除了在不需要的地方添加一些其他代码之外,如果您希望有一个一致的模板,那么保留在3个与事务相关的IF块中也没有什么害处。但是在那种情况下,我仍然建议不要将3个与事务相关的IF块保留给SELECT(仅只读)处理。
当执行2条或更多条DML语句时,您需要按照以下方式使用某些内容(如果希望保持一致,也可以对单个DML操作执行此操作):
CREATE PROCEDURE [SchemaName].[ProcedureName]
(
@Param DataType
...
)
AS
SET NOCOUNT ON;
DECLARE @InNestedTransaction BIT;
BEGIN TRY
IF (@@TRANCOUNT = 0)
BEGIN
SET @InNestedTransaction = 0;
BEGIN TRAN; -- only start a transaction if not already in one
END;
ELSE
BEGIN
SET @InNestedTransaction = 1;
END;
-- { 2 or more DML statements (i.e. INSERT / UPDATE / DELETE) }
IF (@@TRANCOUNT > 0 AND @InNestedTransaction = 0)
BEGIN
COMMIT;
END;
END TRY
BEGIN CATCH
IF (@@TRANCOUNT > 0 AND @InNestedTransaction = 0)
BEGIN
ROLLBACK;
END;
DECLARE @ErrorMessage NVARCHAR(4000) = ERROR_MESSAGE(),
@ErrorState INT = ERROR_STATE(),
@ErrorSeverity INT = ERROR_SEVERITY();
-- optionally concatenate ERROR_NUMBER() and/or ERROR_LINE() into @ErrorMessage
RAISERROR(@ErrorMessage, @ErrorSeverity, @ErrorState);
RETURN;
END CATCH;
当仅执行1个DML语句或仅执行SELECT时,您可以仅执行以下操作:
CREATE PROCEDURE [SchemaName].[ProcedureName]
(
@Param DataType
...
)
AS
SET NOCOUNT ON;
BEGIN TRY
-- { 0 or 1 DML statements (i.e. INSERT / UPDATE / DELETE) }
END TRY
BEGIN CATCH
DECLARE @ErrorMessage NVARCHAR(4000) = ERROR_MESSAGE(),
@ErrorState INT = ERROR_STATE(),
@ErrorSeverity INT = ERROR_SEVERITY();
-- optionally concatenate ERROR_NUMBER() and/or ERROR_LINE() into @ErrorMessage
RAISERROR(@ErrorMessage, @ErrorSeverity, @ErrorState);
RETURN;
END CATCH;
其次,仅当您需要执行多个查询/存储过程并且都需要将它们全部分组为一个原子操作时,才应在应用程序层处理事务。做一个SqlCommand.Execute___
只需要在一个try / catch,而不是在交易中。
但是,仅拨打一个电话时,在应用程序层进行事务处理是否有害?如果它需要MSDTC(Microsoft分布式事务处理协调器),那么在系统上,如果没有特别需要,则可以在应用程序层执行此操作,这会比较重。我个人更喜欢避免基于应用程序层的事务,除非绝对必要,因为这样可以减少孤立事务的可能性(如果在提交或回滚之前应用程序代码出现问题)。我还发现,有时使调试某些情况变得有些困难。话虽这么说,我在进行单个proc时在应用程序层处理事务也没有任何技术上的错误呼叫; 再次,一个DML语句是它自己的事务,并不需要任何明确的事务在任层处理。