我有一个执行大量插入的存储过程,每个插入都会返回一个SCOPE_IDENTITY()
主键值,然后将其用作后续插入的外键。
为了防止由于前一个插入失败而在没有FK的情况下执行插入的情况,我将插入包装在try-catch块中,如下所示:
DECLARE @error INT;
BEGIN TRY
insert statement here...
SET @newId = SCOPE_IDENTITY();
END TRY
BEGIN CATCH
-- assign error value to
SET @error = @@ERROR;
END CATCH
然后下一个插入在继续之前检查错误var:
IF @error = 0
BEGIN
BEGIN TRY
next insert statement here using `@newId` from previous qry...
SET @nextId = SCOPE_IDENTITY();
END TRY
BEGIN CATCH
// assign error value to
SET @error = @@ERROR;
END CATCH
END
等等。这是Try-catch
的合适应用还是过度杀伤?
答案 0 :(得分:1)
如果您打算处理catch返回的错误,那就不是一种杀手锏。如果没有外键,技术上不会出现插入,因此您不会使用try / catch阻止无效数据。因此,如果第一次失败,没有try / catch,第二次插入仍会失败。
为了进一步采用这种方法,您可以考虑使用提交和回滚在事务中进行插入。请参阅此TechNet article中有关不可提交事务的部分。
答案 1 :(得分:1)
来自Here
TRY ... CATCH构造不能跨越多个批次。一个尝试...捕捉 构造不能跨越多个Transact-SQL语句块。对于 例如,TRY ... CATCH构造不能跨越两个BEGIN ... END块 Transact-SQL语句并不能跨越IF ... ELSE构造。
这意味着如果Try...Catch
在内部块中发生,则嵌套的Try..Catch
块不会影响外部Error
块代码。
在这里,您希望向我们提供一个查询到另一个的结果,因此,您最好使用单个Try..Catch
阻止
<强>原因强>
如果TRY块中包含的代码中没有错误, 当TRY块中的最后一个语句完成运行时,控制 在关联的END CATCH之后立即传递给语句 声明。 如果TRY中包含的代码中存在错误 block,control传递给相关CATCH中的第一个语句 块即可。如果END CATCH语句是存储中的最后一个语句 过程或触发器,控制权被传递回语句 调用存储过程或触发触发器。
因此,在这种情况下,在第一次错误发生后没有额外的执行恐惧。
示例:强>
使用嵌套Try...Catch
BEGIN try
declare @param1 as int
BEGIN Try
set @param1='gkjk'
select @param1
END TRY
BEGIN CATCH
SELECT
ERROR_NUMBER() AS ErrorNumber,
ERROR_SEVERITY() AS ErrorSeverity,
ERROR_STATE() as ErrorState,
ERROR_PROCEDURE() as ErrorProcedure,
ERROR_LINE() as ErrorLine,
ERROR_MESSAGE() as ErrorMessage;
END CATCH
BEGIN TRY
select 'hello','i m in',200
END TRY
BEGIN CATCH
SELECT
ERROR_NUMBER() AS ErrorNumber,
ERROR_SEVERITY() AS ErrorSeverity,
ERROR_STATE() as ErrorState,
ERROR_PROCEDURE() as ErrorProcedure,
ERROR_LINE() as ErrorLine,
ERROR_MESSAGE() as ErrorMessage;
END CATCH
END TRY
BEGIN CATCH
SELECT
ERROR_NUMBER() AS ErrorNumber,
ERROR_SEVERITY() AS ErrorSeverity,
ERROR_STATE() as ErrorState,
ERROR_PROCEDURE() as ErrorProcedure,
ERROR_LINE() as ErrorLine,
ERROR_MESSAGE() as ErrorMessage;
END CATCH
它会给你两个结果集。
使用单Try...Catch
BEGIN TRY
declare @param1 as int
set @param1='gkjk'
select @param1
select 'hello','i m in',200
END TRY
BEGIN CATCH
SELECT
ERROR_NUMBER() AS ErrorNumber,
ERROR_SEVERITY() AS ErrorSeverity,
ERROR_STATE() as ErrorState,
ERROR_PROCEDURE() as ErrorProcedure,
ERROR_LINE() as ErrorLine,
ERROR_MESSAGE() as ErrorMessage;
END CATCH
这会给你单一结果。
答案 2 :(得分:1)
这样可以正常工作,但是如果你的意图是在第一次失败后(即在包裹的插入物的捕获范围内)在程序中不再做任何工作,那么你可以:
在2014年及之后,在catch块中使用THROW将重新抛出异常并立即退出当前语句批处理(参见here for details,如果在存储过程中使用,例如,这基本上意味着它将退出当前程序。)
作为使用catch块(2014 +)中的THROW的一个简单示例:
if object_id('tempdb..#TestRethrow') is not null
drop table #TestRethrow;
CREATE TABLE #TestRethrow(ID INT PRIMARY KEY);
BEGIN TRY
INSERT #TestRethrow(ID) VALUES(1);
END TRY
BEGIN CATCH
PRINT 'In catch block 1 - SHOULD NOT FIRE.';
THROW;
END CATCH;
print 'between catch blocks';
BEGIN TRY
-- Unique key violation
INSERT #TestRethrow(ID) VALUES(1);
END TRY
BEGIN CATCH
PRINT 'In catch block 2 - WILL FIRE';
THROW;
PRINT 'At end of catch block 2 - SHOULD NOT FIRE';
END CATCH;
print 'out of catch block 2 - SHOULD NOT FIRE'
select 1;
go
print 'SHOULD FIRE - Another batch (assuming you are using SSMS for example, or another client that separates batches using GO';
go
如果使用SQL 2012或更早版本,您可以使用返回代替throw,但您可能希望首先对异常执行某些操作(即记录它,或使用较旧的RAISERROR语法引发它):< / p>
if object_id('tempdb..#TestRethrow') is not null
drop table #TestRethrow;
CREATE TABLE #TestRethrow(ID INT PRIMARY KEY);
BEGIN TRY
INSERT #TestRethrow(ID) VALUES(1);
END TRY
BEGIN CATCH
PRINT 'In catch block 1 - SHOULD NOT FIRE.';
return;
END CATCH;
print 'between catch blocks';
BEGIN TRY
-- Unique key violation
INSERT #TestRethrow(ID) VALUES(1);
END TRY
BEGIN CATCH
PRINT 'In catch block 2 - WILL FIRE';
return
PRINT 'At end of catch block 2 - SHOULD NOT FIRE';
END CATCH;
print 'out of catch block 2 - SHOULD NOT FIRE'
select 1;
go
print 'SHOULD FIRE - Another batch (assuming you are using SSMS for example, or another client that separates batches using GO';
go