SQL Server尝试捕获

时间:2014-04-25 07:58:36

标签: sql sql-server tsql

我有一个存储过程,每隔x分钟由SQL代理运行一次。存储过程有一个while循环,它读取每一行并对它们执行某些操作。

我想处理在while循环中出现的错误。我需要在Throw块中使用CATCH,因为如果发生错误,SQL Server代理将不会通知。

但问题是如果我使用throw块它会跳出while循环但不会处理其他记录。

如何在while循环中使用TRY CATCH,如果发生错误,它应该继续while循环?

这是我的代码:

WHILE @i<@Count OR @Count IS NULL 
BEGIN
    SELECT @Id = NULL --Clear variable

    SELECT TOP 1 
       @Id = Id,
       @TableNo = [TableNo], 
       @Action = [Action], 
       @RecId = [RecId],
       @NDB = dbo.GetDB(CId)
    FROM 
       dbo.alot WITH (NOLOCK) 
    WHERE 
       CId = @Cid 
       AND Error = 0 
       AND (@Table IS NULL OR TableNo = @Table) 
       AND Tableno <> 50109
    ORDER BY 
       Id

    IF @Id IS NOT NULL 
    BEGIN
       SELECT 
          @SQL = N'EXECUTE @RC = ['+dbo.GetDB(@CId)+'].[dbo].[alot_web] @TableNo, @Action, @RecId, @NaviDB'

       BEGIN TRY
          IF @RecId = '0-761345-27353-4'
          BEGIN
             SELECT 1 / 0; -- Generate an error here.
          END

          EXEC master.dbo.sp_executesql @SQL, N'@TableNo nvarchar(12), @Action tinyint, @RecId nvarchar(36), @NDB varchar(12), @RC int OUTPUT'
          , @TableNo, @Action, @RecId, @NaviDB, @Rc OUTPUT
       END TRY
       BEGIN CATCH
          DECLARE @Description VARCHAR(1024);

          SELECT @Description = 'WebQueue ID: ' + ISNULL(CAST(@Id AS VARCHAR), '') + ' CompanyID: ' + ISNULL(@Cid, '') + ' Action: ' + ISNULL(CAST(@Action AS VARCHAR), '') + ' RecID: ' + ISNULL(CAST(@RecId AS VARCHAR), '') + ' @RC: ' + ISNULL(CAST(@RC AS VARCHAR), '');

          EXEC dbo.LogError @Description;
          THROW;
       END CATCH

       IF @RC = 0 AND @@ERROR = 0 
       BEGIN
         IF EXISTS(SELECT * FROM Queue 
                   WHERE CId = @CId AND [Action] = @Action 
                     AND TableNo = @Tableno AND RecId = @RecID AND Id <> @Id) 
         BEGIN
             DELETE FROM Queue 
             WHERE CId = @CId AND [Action] = @Action AND TableNo = @Tableno 
               AND RecId = @RecID

             SELECT @Ok += @@ROWCOUNT
         END 
         ELSE BEGIN
            DELETE FROM Queue WHERE Id = @Id
            SELECT @Ok += @@ROWCOUNT
         END
      END 
      ELSE BEGIN
         IF EXISTS(SELECT * FROM Queue 
                   WHERE CId = @CId AND [Action] = @Action 
                     AND TableNo = @Tableno AND RecId = @RecID AND Id <> @Id) 
         BEGIN
           UPDATE Queue 
           SET Error = 1 
           WHERE CId = @CId AND [Action] = @Action AND TableNo = @Tableno AND RecId = @RecID

           SELECT @Failed += @@ROWCOUNT
         END 
         ELSE BEGIN
            UPDATE Queue 
            SET Error = 1 
            WHERE Id = @Id

            SELECT @Failed += @@ROWCOUNT    
         END
     END
  END 
  ELSE  
     BREAK

  SELECT @i += 1    

    /*IF @i>0 BEGIN--logging >>
        INSERT INTO [AdminDB].[dbo].[Replication_Log] ([Date],[CId],[Loops],[DurationSS],[Ok],[Failed])
        SELECT Getdate(),@CId,@i,DATEDIFF(ss,@Startdt,getdate()),@Ok,@Failed
    END     */
END

5 个答案:

答案 0 :(得分:0)

您的THROW引发异常,导致SP停止处理。从本质上讲,您正在捕获发生的任何错误,但重新抛出它们确实会突破while循环。 移除它然后sp应该正常继续。

答案 1 :(得分:0)

我的建议是在循环外使用一个循环。 所以TSQL会是这样的,这会有帮助吗?

DECLARE @Continue bit,
    @i INT,
    @Count INT

SELECT @i = 1 ,
@Count =10

SET @Continue =1
While @Continue=1 
BEGIN
    BEGIN TRY
    --YOUR existing while loop
    WHILE @i<@Count OR @Count IS NULL 
    BEGIN
      -- The rest of your code
      IF @i=3
      BEGIN
        set @i= @i/0
      END

      PRINT @i
      SET @i=@i+1
      --Set to false when complete
      IF @i =@Count
        SET @Continue =0
    END
    END TRY
    BEGIN CATCH
         print ERROR_MESSAGE()  
      set @i =@i+1
    END CATCH

END

上述预期结果

1
2
Divide by zero error encountered.
4
5
6
7
8
9

答案 2 :(得分:0)

并非所有错误都可以通过TRY / CATCH捕获。在这种情况下,sp_start_job实际上调用外部过程,这些过程不在SQL Server的错误处理范围内。或者至少那是他们坚持的故事:

http://connect.microsoft.com/SQLServer/feedback/details/362112/sp-start-job-error-handling

请参阅this了解详情

答案 3 :(得分:0)

用CONTINUE替换你的THROW;这样就可以在不取消代码的情况下处理下一条记录。

(EDITED!)

因为您使用

记录错误
EXEC dbo.LogError @Description;

我认为你不需要重新抛出错误。既然你声明你不希望程序结束。

CONTINUE的例子:

DECLARE @intFlag INT
SET @intFlag = 1
WHILE (@intFlag <=5)
BEGIN
PRINT @intFlag
SET @intFlag = @intFlag + 1
CONTINUE;
IF @intFlag = 4 -- This will never executed
BREAK;
END
GO

来源:http://blog.sqlauthority.com/2007/10/24/sql-server-simple-example-of-while-loop-with-continue-and-break-keywords/

编辑:

为您的工作代理人:

在程序的顶部创建一个新变量:

int @ErrorAmount = 0

你的捕获物需要看起来像这样:

   BEGIN CATCH
      DECLARE @Description VARCHAR(1024);

      SELECT @Description = 'WebQueue ID: ' + ISNULL(CAST(@Id AS VARCHAR), '') + ' CompanyID: ' + ISNULL(@Cid, '') + ' Action: ' + ISNULL(CAST(@Action AS VARCHAR), '') + ' RecID: ' + ISNULL(CAST(@RecId AS VARCHAR), '') + ' @RC: ' + ISNULL(CAST(@RC AS VARCHAR), '');

      EXEC dbo.LogError @Description;
      SET @ErrorAmount = @ErrorAmount+1;  --add this line
      CONTINUE;                           --REPLACE THROW with CONTINUE
   END CATCH

在程序结束时添加

if @ErrorAmount > 0
BEGIN
    THROW 6000,'Your message' , 1; 
END

通过这种方式,您的代理将向您显示错误,并且您的整个过程仍然可以完成工作。

答案 4 :(得分:0)

你可以:

  1. 删除THROW。您的脚本将正常继续,并将记录错误
  2. 在脚本结束时检查@Failed,如果合适,请使用SQL代理将注意到的RAISEERROR