T-SQL - 结合Try-Catch和错误处理(事务)

时间:2015-01-10 17:43:15

标签: sql tsql sqltransaction

我正在使用可重复读取隔离级别处理事务。 我想在此事务中包含Try-Catch和错误处理程序功能。 当我运行代码时,我收到一条错误消息: Msg 102,Level 15,State 1,Line 18 'BEGIN'附近的语法不正确。 Msg 102,Level 15,State 1,Line 23 '@errnum'附近的语法不正确。

如何成功完成此交易?要么 编写此交易的正确方法是什么?

这是我现在的工作:

CREATE PROCEDURE ItemFlow (@Name  VARCHAR(50),
                                @aPrice  MONEY,
                                @bPrice MONEY)
AS
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
GO
  BEGIN TRAN
      IF EXISTS (SELECT 1
                 FROM   Cashier
                 WHERE  Item = '@Item')
        UPDATE Cashier
        SET    bPrice = '@bPrice',
               aprice = '@aprice'
        WHERE  Item = '@Item'
      ELSE
        INSERT INTO Cashier
                    (Item, aPrice, bPrice)
        VALUES      ('@Item', '@aPrice', '@bPrice')
                     END
BEGIN TRY
    EXECUTE ItemFlow
END TRY

BEGIN CATCH
    @errnum = ERROR_NUMBER(),
           @severity = ERROR_SEVERITY(),
           @errstate = ERROR_STATE(),
           @proc = ERROR_PROCEDURE(),
           @line = ERROR_LINE(),
           @message = ERROR_MESSAGE()

END CATCH

3 个答案:

答案 0 :(得分:1)

一个问题是终止GO的{​​{1}}。我总是使用create procedure statement / begin存储过程:

end

然后,在调用存储过程时需要参数,或者为它们定义默认值:

CREATE PROCEDURE ItemFlow (@Name  VARCHAR(50),
                           @aPrice  MONEY,
                           @bPrice MONEY)
AS
BEGIN
    SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
    BEGIN TRAN
      IF EXISTS (SELECT 1
                 FROM   Cashier
                 WHERE  Item = '@Item')
        UPDATE Cashier
        SET    bPrice = '@bPrice',
               aprice = '@aprice'
        WHERE  Item = '@Item'
      ELSE
        INSERT INTO Cashier
                    (Item, aPrice, bPrice)
        VALUES      ('@Item', '@aPrice', '@bPrice')
    COMMIT TRAN;
END;  -- ItemFlow

答案 1 :(得分:0)

您的代码存在一些问题:

BEGIN附近的错误

GO是批处理分隔符。它不是有效的T-SQL,只有SSMS才能理解。您实际上是在提交两个查询:

CREATE PROCEDURE ItemFlow (@Name  VARCHAR(50),
                                @aPrice  MONEY,
                                @bPrice MONEY)
AS
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;

BEGIN TRAN
...

如您所见,存储过程主体为空。删除GO以摆脱此问题。

@errnum

附近的错误

您的变量未声明。此外,您必须使用SELECTSET为这些变量分配值:

DECLARE @errnum int,
        @serverity int,
        (etc.)

SELECT @errnum = ERROR_NUMBER(),
       @severity = ERROR_SEVERITY(),
       @errstate = ERROR_STATE(),
       @proc = ERROR_PROCEDURE(),
       @line = ERROR_LINE(),
       @message = ERROR_MESSAGE()

最后一件事:

您有BEGIN TRAN但不是COMMIT TRAN。您的交易在执行结束时仍处于打开状态。

答案 2 :(得分:0)

在实施任何建议的更改之前,请回答:1)为什么要在proc中调用proc本身? 2)为什么要将ERROR_函数设置为变量?你打算用它们吗?如果没有,则无需声明变量。

此外:

  • 显然,GO不能成为其中的一部分。它只是SSMS的批处理分隔符。

  • 您有一个未使用的名为@Name的输入参数,以及一个未声明的名为@Item的变量。我怀疑它们是相同的,所以我使用@Item作为输入参数而不是@Name

  • 在三个查询中使用输入参数作为字符串文字,这没有任何意义。您需要从它们周围删除单引号,以便它们可以充当变量。

  • 并且,请 NOT 将TRY / CATCH逻辑与交易分开!

假设调用无限循环没有真正意图(proc调用自身没有任何条件可以停止),你的代码应如下所示:

CREATE PROCEDURE ItemFlow
(
  @Item  VARCHAR(50),
  @aPrice  MONEY,
  @bPrice MONEY
)
AS
SET NOCOUNT ON;

SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;

BEGIN TRY

  BEGIN TRAN

  IF EXISTS (SELECT 1
             FROM   Cashier
             WHERE  Item = @Item)
  BEGIN
        UPDATE Cashier
        SET    bPrice = @bPrice,
               aprice = @aPrice
        WHERE  Item = @Item;
  END;
  ELSE
  BEGIN
        INSERT INTO Cashier
                    (Item, aPrice, bPrice)
        VALUES      (@Item, @aPrice, @bPrice);
  END;

  COMMIT TRAN;

END TRY
BEGIN CATCH

  IF (@@TRANCOUNT > 0)
  BEGIN
    ROLLBACK TRAN;
  END;

  THROW;

END CATCH;
SQL Server 2012中引入了

THROW。如果您使用的是2005 - 2008 R2中的任何内容,请将THROW替换为:

DECLARE @ErrMessage NVARCHAR(4000);
SET @ErrMessage = ERROR_MESSAGE();
RAISERROR(@ErrMessage, 16, 1);
RETURN;