使用存储过程时预测致命错误
Posted
技术标签:
【中文标题】使用存储过程时预测致命错误【英文标题】:Anticipating fatal errors when using stored procedures 【发布时间】:2015-02-28 13:34:38 【问题描述】:请看下面的代码:
ALTER PROCEDURE GetPerson
AS
BEGIN TRANSACTION;
BEGIN TRY
DECLARE @TestVariable1 AS INT;
DECLARE @TestVariable2 AS INT;
SET @TestVariable1 = 1;
SET @TestVariable2 = 0;
DECLARE @TestVariable3 AS INT;
SET @TestVariable3 = @TestVariable1 / @TestVariable2;
PRINT 'transaction committed';
END TRY
BEGIN CATCH
ROLLBACK;
PRINT 'transaction rolled back';
END CATCH
我可以使用以下命令运行它:
EXEC GetPerson
事务成功回滚,因为你不能除我的零。
现在看下面的代码:
ALTER PROCEDURE GetPerson
AS
BEGIN TRANSACTION;
BEGIN TRY
CREATE TABLE #Test
(
test INT
);
SELECT TOP 1 *
FROM #Test;
SELECT TOP 1 *
FROM person.person;
DROP TABLE #Test;
SELECT TOP 1 *
FROM #Test;
PRINT 'transaction committed';
END TRY
BEGIN CATCH
ROLLBACK;
PRINT 'transaction rolled back';
END CATCH
如果我运行相同的评论:
EXEC GetPerson
然后我得到一个错误:
消息 208,级别 16,状态 0,过程 GetPerson,第 11 行 无效的对象名称“#Test”。
消息 266,级别 16,状态 2,过程 GetPerson,第 11 行 EXECUTE 之后的事务计数表明 BEGIN 和 COMMIT 语句的数量不匹配。先前计数 = 0,当前计数 = 1
我了解并非所有错误都被 TRY 子句捕获,如下所述:https://msdn.microsoft.com/en-gb/library/ms178592.aspx
当 RAISERROR 在 TRY 块中以 11 或更高的严重性运行时,它会将控制权转移到关联的 CATCH 块
我的问题是:您如何处理这些问题?我知道在我的代码中,尝试从不再存在的临时表中进行选择是一个明显的开发人员错误。但是,如果还有其他我应该处理的错误,我会徘徊。
在调用存储过程时这样做是否可以接受:
EXECUTE GetPerson ;
IF @@trancount >= 1
ROLLBACK;
或者我可以这样做:
EXEC GetPerson
IF @@trancount >= 1
INSERT INTO dbLog ("Trancount greater than 1 after running GetPerson")
【问题讨论】:
FWIW, this 可能会提供一些关于事务和错误处理的额外见解。 【参考方案1】:错误 208 是一个编译错误。您的代码永远不会运行,因为它甚至无法编译。这与要求 C# 程序捕获编译错误相同。
此外,如果您想要一个正确的 try/catch 块来处理异常和事务,请参阅Exception handling and nested transactions:
create procedure [usp_my_procedure_name]
as
begin
set nocount on;
declare @trancount int;
set @trancount = @@trancount;
begin try
if @trancount = 0
begin transaction
else
save transaction usp_my_procedure_name;
-- Do the actual work here
lbexit:
if @trancount = 0
commit;
end try
begin catch
declare @error int, @message varchar(4000), @xstate int;
select @error = ERROR_NUMBER(), @message = ERROR_MESSAGE(), @xstate = XACT_STATE();
if @xstate = -1
rollback;
if @xstate = 1 and @trancount = 0
rollback
if @xstate = 1 and @trancount > 0
rollback transaction usp_my_procedure_name;
raiserror ('usp_my_procedure_name: %d: %s', 16, 1, @error, @message) ;
end catch
end
显然这不会处理语法/绑定错误(如 208),但至少会处理正确的过程。请记住,在 CATCH 块中检查 XACT_STATE()
是强制。考虑像 1205(死锁)这样的错误,其中 CATCH 块在事务回滚后运行。您还需要尊重和处理调用者的交易。
【讨论】:
谢谢+1。我有点困惑。我将解释问题的原因。我有一个需要大约两个小时才能运行的存储过程。在第一行,它创建了一个临时表。最近在运行大约十分钟后,它失败并出现错误:“无效的对象名称'#TableName”。因此这表明它确实尝试运行?以上是关于使用存储过程时预测致命错误的主要内容,如果未能解决你的问题,请参考以下文章
oracle创建存储过程时,提示错误是:错误(5,18): PL/SQL: ORA-00947: 没有足够的值?代码如下:
oracle创建存储过程时,提示错误是:错误(5,18): PL/SQL: ORA-00947: 没有足够的值?代码如下: