简体   繁体   中英

SQL Server Try Catch

I have a stored procedure that is run by the SQL Agent every x minutes. The stored procedure has a while loop that reads each row and does something with them.

I want to handle errors as they occur in the while loop. I need to use Throw in the CATCH block because then SQL Server Agent will not notify if an error occurred.

But the problem is if I use the throw block it breaks out of the while loop but does not process the other records.

How can I use TRY CATCH in a while loop and if an error occurs it should continue with while loop?

This is my code:

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

Your THROW raises an exception which causes the SP to stop processing. Essentially you are catching any errors that occur but rethrowing them which will indeed break out of the while loop. Remove it and the sp should then just continue as normal.

My suggestion is use one more loop outside the loop you have. So the TSQL will be something like this, will this helps?

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

Expected result from above

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

Not all errors can be caught by TRY/CATCH. In this case, sp_start_job actually calls external procedures, and these are outside the bounds of SQL Server's error handling. Or at least that's the story that they're sticking to:

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

Please refer this for more details

Replace your THROW with CONTINUE; this way the next record will be processed without Canceling the code.

(EDITED!)

since you log your errors with

EXEC dbo.LogError @Description;

i think you don't need to rethrow the error. Since you stated you don't want the program to end.

example of 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

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

EDIT:

for your job agent:

create a new variable at the top of your procedure :

int @ErrorAmount = 0

your catch would need to look like this:

   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

at the end of your procedure Add

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

this way your Agent will show you the error and your whole process still did the job.

You could:

  1. Remove the THROW . Your script will continue as normal and errors will be logged
  2. At the end of the script check @Failed and if appropriate use RAISEERROR which will be noted by the SQL Agent

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM