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
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:
THROW
. Your script will continue as normal and errors will be logged @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.