1

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
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
user3514987
  • 210
  • 3
  • 9
  • why not catch the errors and log them into a table? – Ric Apr 25 '14 at 08:03
  • This is what I do. The problem is that I want the while loop to continue with the other rows if an error occurs and not simply jump out of the while loop to go to the BEGIN CATCH END CATCH block. I tried adding the try catch block inside the while loop but still no luck. – user3514987 Apr 25 '14 at 08:05
  • Post the code that you are using. you should be able to continue processing within the while loop if your `TRY CATCH` is set up correctly. – Ric Apr 25 '14 at 08:06
  • Your throw raises another exception as per documentation: http://technet.microsoft.com/en-us/library/ee677615.aspx remove it so that your sp can continue! – Ric Apr 25 '14 at 08:13
  • Just remove the `THROW;` line and you should be fine. – Fedor Hajdu Apr 25 '14 at 08:15
  • I need to use THROW; Becuase if it don't then the SQL Server Agent will not registere it if an error occured. – user3514987 Apr 25 '14 at 08:20
  • 1
    Then the code you are running works just fine! you cannot expect your code to continue executing after an error is raised by the throw. – Ric Apr 25 '14 at 08:21
  • Is there any other alternatives? – user3514987 Apr 25 '14 at 08:23
  • 1
    If you have handled the error you should not THROW an exception, as knowledge of the error is captured in your error logging, and SQL Server Agent doesn't need to know. THROW should be used when you want processing to stop, or to be captured at a higher level. – Kevin Hogg Apr 25 '14 at 08:37

5 Answers5

0

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.

Ric
  • 12,855
  • 3
  • 30
  • 36
0

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
tsohtan
  • 830
  • 1
  • 13
  • 33
0

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

Community
  • 1
  • 1
Janty
  • 1,708
  • 2
  • 15
  • 29
0

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.

Schuere
  • 1,579
  • 19
  • 33
0

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
Nick.Mc
  • 18,304
  • 6
  • 61
  • 91