我有一个存储过程,我需要转换为类型,但不知道转换是否会成功。在命令式语言中,我会使用某种TryCast
模式。我认为这在T-SQL中是等效的:
begin try
select cast(@someValue as SomeType)
end try begin catch end catch
从表面上看,它似乎是等效的。如果@SomeTypeVar
未初始化且投射失败,我会NULL
使用;如果演员成功,则为正确的值。
我在存储过程中使用了相同的代码,但是产生了一个错误:Msg 2812, Level 16, State 62, Line 20. The current transaction cannot be committed and cannot support operations that write to the log file. Roll back the transaction.
一些研究引发了我关于Stack Overflow和T-SQL中try-catch
失败的时间表的其他问题:
TRY ... CATCH构造不会捕获以下条件:
- 严重性为10或10的警告或信息性消息 降低。
- 严重性为20或更高的错误,用于停止SQL 会话的服务器数据库引擎任务处理。如果有错误 发生严重性为20或更高的数据库连接 没有被打乱,TRY ...... CATCH将处理错误。
- 注意事项,如 客户端中断请求或破坏的客户端连接。
- 当 会话由系统管理员使用KILL结束 声明。
CATCH不处理以下类型的错误 阻止它们发生在与TRY ... CATCH相同的执行级别 构建体:
- 编译阻止批处理的错误,例如语法错误 运行。
- 语句级重新编译期间发生的错误,例如 作为编译后发生的对象名称解析错误,因为 延迟名称解析。
这些错误将返回到该级别 运行批处理,存储过程或触发器。
起初,我以为我陷入了语句级重新编译桶(因为我的错误级别是16),直到我试图将问题一分为二。最小的再现如下:
create procedure failsInTransactions
as
begin
begin try
select cast(@someValue as SomeType)
end try begin catch end catch
end
和调用代码:
begin tran
exec failsInTransactions
commit
这产生了我上面讨论的错误。但是,我记得如果存储过程没有任何参数,你可以在没有exec的情况下调用它。这样:
begin tran
failsInTransactions
commit
成功Command(s) completed successfully.
进一步的实验导致我再次出现16级错误:
begin try
select 1/0
end try begin catch end catch
在两种情况下都有效,不产生任何输出行。
我有两个问题:
exec
时调用该过程有不同的行为?答案 0 :(得分:3)
只有当EXECUTE关键字是批处理中的第一个语句时,它才是可选的。它与参数无关,在所有其他上下文中都是必需的。 Microsoft从Sybase代码库以及其他许多松散的T-SQL解析规则继承了这种奇怪的行为。我建议你遵循严格的T-SQL编码风格来避免陷阱。
下面的代码运行时没有错误,因为它根本没有执行proc。由于没有分号语句终止符,因此存储过程名称将成为BEGIN TRAN语句的一部分,并被解释为事务名称。
begin tran
failsInTransactions
commit
如果添加语句终止符,您将在编译期间获得预期的语法错误,这将引导您沿着路径指定EXEC。
begin tran;
EXEC failsInTransactions;
commit;
请注意,不推荐使用语句终结符,因此我建议您养成指定它们的习惯。请参阅https://www.dbdelta.com/always-use-semicolon-statement-terminators/。