簡體   English   中英

SQL Server嘗試捕獲

[英]SQL Server Try Catch

我有一個存儲過程,該存儲過程由SQL代理每x分鍾運行一次。 該存儲過程具有一個while循環,該循環讀取每一行並對它們執行某些操作。

我想處理while循環中發生的錯誤。 我需要在CATCH塊中使用Throw ,因為這樣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

您的THROW引發了一個異常,該異常導致SP停止處理。 本質上,您正在捕獲發生的任何錯誤,但是將它們重新拋出,這確實會打破while循環。 刪除它,然后sp應該照常繼續。

我的建議是在循環之外再使用一個循環。 因此,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

TRY / CATCH不能捕獲所有錯誤。 在這種情況下,sp_start_job實際上會調用外部過程,而這些過程超出了SQL Server錯誤處理的范圍。 或至少這就是他們堅持的故事:

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

請參閱以獲取更多詳細信息

用CONTINUE替換THROW; 這樣,將在不取消代碼的情況下處理下一條記錄。

(編輯!)

因為您記錄錯誤

EXEC dbo.LogError @Description;

我認為您不需要重新拋出錯誤。 既然您說過,您不希望程序結束。

繼續的示例:

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

這樣,您的代理將向您顯示錯誤,並且您的整個過程仍然可以完成任務。

你可以:

  1. 刪除THROW 您的腳本將繼續正常運行,並且將記錄錯誤
  2. 在腳本末尾檢查@Failed並在適當的情況下使用RAISEERROR ,SQL Agent會指出該錯誤

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM