SQL Server TRY CATCH FINALLY

44

我有一个场景,需要类似于.NET的try-catch-finally块。

在我的try中,我将CREATE一个#temp表,对其进行INSERT数据处理,以及基于#temp处理其他数据集。

在中使用RAISERROR。是否可能有一个FINALLY块来DROP #temp?下面是伪代码:

BEGIN TRY

  CREATE TABLE #temp
  (
     --columns
  )
  --Process data with other data sets

END TRY
BEGIN CATCH

  EXECUTE usp_getErrorMessage

END CATCH
BEGIN FINALLY

  DROP TABLE #temp

END FINALLY

1
不,没有FINALLY。你在文档中看到任何相关的参考了吗?你确定要明确删除你的#temp表吗? - Aaron Bertrand
8个回答

43

尽管与FINALLY不完全相同,但Try-Catch的T-SQL版本确实允许在END CATCH语句结束后执行需要在Try和Catch块之后执行的代码。 以问题代码为例:

    BEGIN TRY
      CREATE TABLE #temp
       (
         --columns
       )
      --Process data with other data sets
    END TRY
    BEGIN CATCH
    EXECUTE usp_getErrorMessage
    END CATCH;

IF OBJECT_ID('tempdb..#temp') IS NOT NULL -- Check for table existence
    DROP TABLE #temp;

无论 Try 或 Catch 执行,DROP TABLE 命令都会执行。

参见:BOL Try...Catch


使用Object_ID()函数与#表 - Rhdr
1
这是对这个特定问题的正确答案。 - Toolsmythe
4
在SQL Server 2016或更高版本中,您可以使用 DROP TABLE IF EXISTS #temp 这个一行代码来代替使用带有 OBJECT_IDIF 语句。 - BassGod
如果“EXECUTE usp_getErrorMessage”引发错误,那么这个操作不会删除表格,是吗? - CursedGoat
@CursedGoat,无论Try/Catch中发生了什么,IF块都会执行。它具有IF是因为如果临时表未被创建并且您只使用了DROP语句,那可能会引发错误。 - Dave Bennett
这个方法有效的原因是T-SQL在抛出错误时不会停止执行。如果你运行以下查询:DECLARE @x INT DECLARE @y INT DECLARE @z INTSET @x = 5 SET @y = 0 SET @z = @x / @y SELECT @x, @y, @z,你会注意到即使发生除零错误,select语句仍然会执行。而对于其他语言如C#、Java等,未捕获的异常将结束程序的执行。 - alaniane

16

你可以声明一个表变量来替代创建一个表(当查询结束时,表变量会自动消失)。

BEGIN TRY
DECLARE @temp TABLE
(
    --columns
)
--do stuff
END TRY
BEGIN CATCH
--do other stuff
END CATCH

1
当您处理的数据较小或者您不需要 SQL Server 执行任何优化时,这是一个不错的方法,因为表变量不会创建统计信息等。虽然它解决了确保始终清理的问题,但如果他们真的需要使用临时表(如示例所示),它将无法解决。我不喜欢使用 GOTO 语句,但在 T-SQL 中我不确定您如何实现它。GOTO l_Exitl_Exit: DROP TABLE #temp - Jim Scott
我刚遇到了一个问题,表变量由于某些原因被卡住了。查询本身在2秒内执行完毕,临时表需要3秒钟,但是表变量根本无法执行,经过10分钟后我取消了它的执行... - KoViMa
1
好的例子。但是请记住,在处理大量数据时,变量表并不总是最有效的。 - Damian
try/catch方法无法处理常见的资源分配/释放任务,例如sp_OACreate/sp_OADestroysp_xml_preparedocument/sp_xml_removedocument、HTTP API中的会话管理等等。特别是当它们一起执行时。确实需要异常处理中的finally概念。 - Aleksey F.

7

没有FINALLY的等效语句。
一个替代方法可能是使用表变量,但并不完全相同,必须根据情况进行评估。
有一个SO问题详细说明了如何做出明智的选择。
使用表变量时,您无需像临时表一样进行清理操作。


2
临时表的替代方案可能是临时表?我想你指的是表变量。此外,这个问题更好,有更多细节 - Aaron Bertrand

5
“FINALLY”通常情况下,与在TRY/CATCH之后紧跟着“final”代码(没有正式的“FINALLY”块)功能上是相同的。不同之处在于,TRY/CATCH块中的某些内容可能会导致执行结束,例如返回语句。
例如,一种我使用过的模式是打开游标,然后将使用游标的代码放在TRY块中,而游标关闭/释放则在TRY/CATCH块之后。如果这些块不会退出正在执行的代码,那么这样做是可以正常工作的。但是,如果TRY CATCH块执行了RETURN(听起来像个坏主意),如果有FINALLY块,则会被执行,但由于T-SQL要求将最终代码放置在TRY/CATCH之后,如果这些代码块导致执行结束,那么该最终代码就不会被调用,可能导致状态不一致。
因此,虽然通常情况下你可以直接将代码放在TRY/CATCH之后,但如果这些块中的任何内容可能会在未经过清理代码的情况下终止,就会出现问题。

2
本地临时表(例如“#Temp”)在SQL连接结束时会自动删除。最好实现明确的DROP命令,但如果不执行,表仍将被删除。
如果您必须确保尽快执行DROP,则必须在CATCH子句中重复DROP命令,因为没有FINALLY
-- create temp table;
BEGIN TRY
    -- use temp table;
    -- drop temp table;
END TRY
BEGIN CATCH
    -- drop temp table;
    THROW;  -- rethrow the error
END CATCH

表变量是一个替代方案:当变量超出作用域时它们就被删除了。然而,表变量不支持统计信息,因此,如果表变量很大并且在多个查询中使用,则可能性能不如临时表。


0
在这种情况下,正确的答案是由@Dave Bennett提出的;在TRY/CATCH块之后检查表的存在并删除它。
但是,如果您正在CATCH中引发异常并且需要进行一些“FINALLY”类型的处理怎么办?
是否可以简单地在CATCH中设置一个变量,并在从CATCH中退出后检查它?
DECLARE @is_error BIT = 0;

BEGIN TRY

  --Process data with other data sets

END TRY
BEGIN CATCH
  -- Your exception handling code here
  
  SET @is_error = 1;

END CATCH

-- Your "FINALLY" code here.

-- Then Check if you need to RAISERROR
IF @is_error = 0
BEGIN
    -- Your success code 
END
ELSE
BEGIN
    -- Your fail code
    -- RAISERROR
END;

“这可能就是那么简单吗?”——这是一个问题,不是答案。如果您测试了代码并确认它可以工作,请相应地编辑您的回答,否则它将被标记为“不是答案”。 - Gert Arnold

0
使用 T-SQL,在 TRY-CATCH 块之后放置的代码将被执行。因此,答案很简单,只需在 END CATCH 后面添加 DROP TABLE IF EXISTS #temp,就像这样:
BEGIN TRY
  CREATE TABLE #temp(
     --columns
  )
  --Process data with other data sets
END TRY
BEGIN CATCH
  EXECUTE usp_getErrorMessage
END CATCH
-- Anything under this line will execute regardless of if the code reached the CATCH block or not.
DROP TABLE IF EXISTS #temp;  -- Works in SQL Server 2016+, for older versions see here: https://dev59.com/C18e5IYBdhLWcg3wZpvw#31171650

0

使用自定义错误编号来表示没有真正的错误,只是最终代码吗?

-- create temp table;
BEGIN TRY
    -- use temp table;
    THROW 50555;
END TRY
BEGIN CATCH
    -- drop temp table;
    IF ERROR_NUMBER() <> 50555
        THROW;  -- rethrow the error
END CATCH

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接