【问题标题】:Is it necessary to put transactions in this code?是否有必要将事务放在此代码中?
【发布时间】:2016-03-17 12:01:34
【问题描述】:

我的主管要求我不要在此代码中放置事务和提交等,因为他说在此过程中放置​​事务是没有用的。他经验丰富,我不能直接和他争论,所以需要你的意见吗?

ALTER PROCEDURE [Employee].[usp_InsertEmployeeAdvances](
      @AdvanceID        BIGINT,
      @Employee_ID      INT,
      @AdvanceDate      DATETIME,
      @Amount           MONEY,
      @MonthlyDeduction MONEY,
      @Balance          MONEY,
      @SYSTEMUSER_ID    INT,
      @EntryDateTime    DATETIME = NULL,
      @ProcedureType    SMALLINT)
AS
BEGIN
    BEGIN TRY
        BEGIN TRANSACTION [Trans1]
        IF EXISTS
           (
              SELECT *
              FROM Employee.Advance
              WHERE AdvanceID = @AdvanceID
           )
        BEGIN
            --UPDATION OF THE RECORD
            IF @ProcedureType = 1
            BEGIN
                SET @Amount = @Amount * -1;
            END
            UPDATE Employee.Advance
            SET
                  Employee_ID = @Employee_ID,
                  AdvanceDate = @AdvanceDate,
                  Amount = @Amount,
                  MonthlyDeduction = @MonthlyDeduction,
                  Balance = @Balance,
                  EntryDateTime = GETDATE()
            WHERE AdvanceID = @AdvanceID
        END
        ELSE
        BEGIN
            DECLARE @LastRecordID INT
            DECLARE @LastBalance MONEY
            SET @LastRecordID =
               (
                  SELECT MAX(EA.AdvanceID)
                  FROM Employee.Advance EA
                  WHERE EA.Employee_ID = @Employee_ID
               )
            SET @LastBalance =
               (
                  SELECT EA.Balance
                  FROM Employee.Advance EA
                  WHERE EA.AdvanceID = ISNULL(@LastRecordID, 0)
               )
            IF(@ProcedureType = 0) --Advances
            BEGIN
                SET @Balance = ISNULL(@LastBalance, 0) + @Amount
                INSERT INTO Employee.Advance
                                            (Employee_ID,
                                             AdvanceDate,
                                             Amount,
                                             MonthlyDeduction,
                                             Balance,
                                             User_ID,
                                             EntryDateTime
                                            )
                VALUES
                       (@Employee_ID,
                        @AdvanceDate,
                        @Amount,
                        @MonthlyDeduction,
                        @Balance,
                        @SYSTEMUSER_ID,
                        GETDATE())
            END
            ELSE --Receivings
            BEGIN
                IF NOT EXISTS
                   (
                      SELECT *
                      FROM Employee.Advance EA
                      WHERE EA.Employee_ID = @Employee_ID
                            AND EA.Balance > 0
                            AND EA.AdvanceID =
                         (
                            SELECT MAX(AdvanceID)
                            FROM Advance
                            WHERE Employee_ID = @Employee_ID
                         )
                   )
                BEGIN
                    RAISERROR('This Employee has no advances history', 16, 1)
                    RETURN
                    --Select 0                  
                END
                ELSE
                BEGIN
                    SET @Balance = ISNULL(@LastBalance, 0) - @Amount
                    INSERT INTO Employee.Advance
                                                (Employee_ID,
                                                 AdvanceDate,
                                                 Amount,
                                                 MonthlyDeduction,
                                                 Balance,
                                                 User_ID,
                                                 EntryDateTime
                                                )
                    VALUES
                           (@Employee_ID,
                            @AdvanceDate,
                            -1 * @Amount,
                            @MonthlyDeduction,
                            @Balance,
                            @SYSTEMUSER_ID,
                            GETDATE())
                END
            END
        END
        COMMIT TRANSACTION [Trans1]
    END TRY
    BEGIN CATCH
        ROLLBACK TRANSACTION [Trans1]
    END CATCH
END

【问题讨论】:

  • 简单地说,在你的情况下没有......
  • 有一点可能是,所采取的所有操作都是单个操作(取决于您的 IF 条件)。如果有错误,它无论如何都应该回滚......如果你的过程是用外部事务调用的,它也相当无用......

标签: sql sql-server database sql-server-2008 tsql


【解决方案1】:
ALTER PROCEDURE [Employee].[usp_InsertEmployeeAdvances]
(
      @AdvanceID        BIGINT,
      @Employee_ID      INT,
      @AdvanceDate      DATETIME,
      @Amount           MONEY,
      @MonthlyDeduction MONEY,
      @Balance          MONEY,
      @SYSTEMUSER_ID    INT,
      @EntryDateTime    DATETIME = NULL,
      @ProcedureType    SMALLINT
)
AS BEGIN

    SET NOCOUNT ON

    IF EXISTS (
        SELECT 1
        FROM Employee.Advance
        WHERE AdvanceID = @AdvanceID
    )
    BEGIN

        UPDATE Employee.Advance
        SET
                Employee_ID = @Employee_ID,
                AdvanceDate = @AdvanceDate,
                Amount = CASE WHEN @ProcedureType = 1 THEN -@Amount ELSE @Amount END,
                MonthlyDeduction = @MonthlyDeduction,
                Balance = @Balance,
                EntryDateTime = GETDATE()
        WHERE AdvanceID = @AdvanceID

    END
    ELSE BEGIN

        DECLARE
              @LastRecordID INT
            , @LastBalance MONEY
            , @IsBalance BIT

        SELECT @LastRecordID = MAX(AdvanceID)
        FROM Employee.Advance
        WHERE Employee_ID = @Employee_ID

        SELECT
            @LastBalance = Balance,
            @IsBalance = CASE WHEN Balance > 0 THEN 1 ELSE 0 END
        FROM Employee.Advance
        WHERE AdvanceID = ISNULL(@LastRecordID, 0)

        IF ISNULL(@IsBalance, 0) = 0 BEGIN

            RAISERROR('This Employee has no advances history', 16, 1)
            RETURN 

        END
        ELSE BEGIN

            INSERT INTO Employee.Advance(Employee_ID, AdvanceDate, Amount, MonthlyDeduction, Balance, [User_ID], EntryDateTime)
            SELECT
                @Employee_ID,
                @AdvanceDate,
                CASE WHEN @ProcedureType = 0 THEN @Amount ELSE -@Amount END,
                @MonthlyDeduction,
                ISNULL(@LastBalance, 0) + CASE WHEN @ProcedureType = 0 THEN @Amount ELSE -@Amount END,
                @SYSTEMUSER_ID,
                GETDATE()

        END

    END

END

【讨论】:

    【解决方案2】:

    这是一个更改的答案,因为我没有阅读整个问题

    如果没有事务,如果它被同时调用并且记录不存在,那么两者都可以插入并且一个可能会得到@Balance 错误

    是的,交易是有目的的

    【讨论】:

      【解决方案3】:

      我永远不会仅仅因为您永远不知道它何时会被合并到另一个工作中而将事务放在特定的工作中。只有调用者才能知道。举个陈旧的例子,你可能认为创建订单和添加商品应该是一个足够公平的交易。在跟踪中,某些调用函数可能希望将信用检查或帐户创建的结果作为其中的一部分。因此,基本上在低级别,您无法知道*是否处于事务的上下文中,因此严格提交并没有多大意义。同样,回滚有点错误——你永远不知道特定错误在调用者的上下文中有多致命,所以只需抛出异常并让客户端决定如何管理它。

      *你可以,但通常更容易不在乎。

      【讨论】:

        【解决方案4】:

        由于现在编写存储过程,因此事务不会添加任何内容。只有一个修改会运行,如果它失败了,它会自动回滚。还需要注意的是,修改后 不会发生任何事情。如果在其中一个更新之后有一些额外的代码,那么这可能需要进行交易。

        但是,进行该交易的成本是多少?你希望这段代码永远不会改变吗?未来的变化可能会使交易变得必要,但并不明显。鉴于您的存储过程的性质,我发现很难相信交易存在不利的一面。

        在TRY..CATCH 块内处理RAISERROR:

        BEGIN TRANSACTION
        
        BEGIN TRY
        
        
            COMMIT TRANSACTION
        END TRY
        BEGIN CATCH
            IF (@@TRANCOUNT > 0)
                ROLLBACK TRANSACTION
        
            EXEC dbo.LogErrorAndRethrow
        END CATCH
        

        LogErrorAndRethrow 的代码如下所示:

        CREATE PROCEDURE dbo.LogErrorAndRethrow
            @LogError BIT = 1
        AS
        BEGIN
            DECLARE
                @error_number   INT,
                @error_message  NVARCHAR(MAX),
                @error_severity INT,
                @error_state    INT,
                @error_procedure NVARCHAR(200),
                @error_line     INT
        
            SELECT
                @error_number = ERROR_NUMBER(),
                @error_message = ERROR_MESSAGE(),
                @error_severity = ERROR_SEVERITY(),
                @error_state = ERROR_STATE(),
                @error_procedure = COALESCE(ERROR_PROCEDURE(), '<Unknown>'),
                @error_line = ERROR_LINE()
        
            IF (@LogError = 1)
            BEGIN
                EXEC dbo.InsertAuditLog
                    @UserID = 0,
                    @AuditType = 'Error',
                    @ModuleName = @error_procedure,
                    @Message = @error_message
            END
        
            -- Rebuild the error message with parameters so that all information appears in the RAISERROR
            SELECT @error_message = N'Error %d, Level %d, State %d, Procedure %s, Line %d, ' + 'Message: ' + ERROR_MESSAGE()
        
            RAISERROR(@error_message, @error_severity, 1, @error_number, @error_severity, @error_state, @error_procedure, @error_line)
        END
        

        InsertAuditLog 基本上只是插入到日志表中。

        【讨论】:

        • 非常感谢。实际上他告诉我删除它,因为 RAISE 错误在 TRY CATCH 中不起作用
        • 嗯,是的。您需要处理 CATCH 块内的错误,但这应该是意料之中的。我将在我的答案中添加一些内容以显示示例。
        • 如果你只是想重新抛出错误,那么你可以在没有一个完整的单独存储过程的情况下做到这一点。
        猜你喜欢
        • 2014-01-25
        • 2021-11-13
        • 2020-02-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-02-24
        • 1970-01-01
        相关资源
        最近更新 更多