我有一个存储过程,每隔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
答案 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
编辑:
为您的工作代理人:
在程序的顶部创建一个新变量:
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)
你可以:
THROW
。您的脚本将正常继续,并将记录错误@Failed
,如果合适,请使用SQL代理将注意到的RAISEERROR