使用try / catch进行动态SQL备份命令时如何记录错误详细信息


10

在使用try catch和动态sql的存储过程中发出备份命令时,与直接运行备份命令相比,错误消息非常普遍。

在SP中尝试/捕获:

    begin try
        execute sp_executesql @sql;  -- a backup command
    end try
    begin catch  
        print ERROR_MESSAGE();  -- save to log, etc.
    end catch

结果是

50000:usp_Backup:117:BACKUP DATABASE异常终止。

wheareas发出raw命令:

    backup DATABASE someDb to disk...

结果更好的细节:

查找错误-SQL Server数据库错误:文件“ H:\ FolderName \ Filename.bak:” 112上发生了不可恢复的I / O错误(磁盘上没有足够的空间。)。

有没有办法将这些详细信息捕获到存储过程中的变量中(进行记录,传递回调用方以进行重试逻辑)?似乎详细信息正在通过消息通道传递,但我希望它们在SP中可用。


您可能希望看到这一点:stackoverflow.com/questions/5966670/...
8KB

Answers:


13

BACKUP DATABASE产生错误时,它实际上产生两个。不幸的TRY/CATCH是无法捕获第一个错误;它仅捕获第二个错误。

我怀疑捕获失败备份背后的真正原因的最佳选择是通过SQLCMD-o用于将输出发送到文件),SSIS,C#,PowerShell等自动执行备份。所有这些都将为您提供更好的控制所有捕获功能错误。

评论中的SO答案建议使用DBCC OUTPUTBUFFER-尽管有可能,但这似乎根本不是儿童游戏。可以在Erland Sommarskog的站点上随意使用此过程,但与结合使用似乎仍然效果不佳TRY/CATCH

我似乎能够捕获错误消息的唯一方法spGET_LastErrorMessage是是否抛出实际错误。如果将其包装在a中TRY/CATCH,错误将被吞没,并且存储过程将不执行任何操作:

BEGIN TRY
  EXEC sp_executesql N'backup that fails...';
END TRY
BEGIN CATCH
  EXEC dbo.spGet_LastErrorMessage;
END CATCH

在SQL Server <2012中,您不能自己重新引发该错误,但可以在SQL Server 2012及更高版本中。因此,这两种变体有效:

CREATE PROCEDURE dbo.dothebackup
AS
BEGIN
  SET NOCOUNT ON;
  EXEC sp_executesql N'backup that fails...';
END
GO

EXEC dbo.dothebackup;
EXEC dbo.spGET_LastErrorMessage;

或在2012年及以后的版本中,此方法有效,但在很大程度上未能达到的目的TRY/CATCH,因为仍会引发原始错误:

CREATE PROCEDURE dbo.dothebackup2
AS
BEGIN
  SET NOCOUNT ON;
  BEGIN TRY
    EXEC sp_executesql N'backup that fails...';
  END TRY
  BEGIN CATCH
    THROW;
  END CATCH
END
GO

EXEC dbo.dothebackup2;
EXEC dbo.spGET_LastErrorMessage;

当然,在这两种情况下,错误仍然会抛出给客户端。因此,如果您要TRY/CATCH避免这种情况,除非有我不介意的漏洞,否则恐怕您必须做出选择……要么为用户提供错误信息,要么能够捕获有关以下内容的详细信息:或抑制错误和实际原因。


尽管很荒谬,但是Sommarskog的方法似乎不是不可能的,如果我只是想在接口内为调用者提供一些上下文的话。比开始单独的流程更好。您是说它在TRY / CATCH中不起作用吗?
crokusek 2012年

@crokusek我尝试了一种变体,结果结果为空。我今天再拍。
亚伦·伯特兰

在没有错误的情况下,LastErrorMessage()是否从会话中获取任何先前错误的结果?然后,如果最后两个执行程序以脚本形式运行,则第一个执行程序可以包装在try / catch和catch中,设置变量,然后重新抛出。然后,仅在设置了变量的情况下才调用LastError。假设重新抛出不会跳过第二次调用,我认为在脚本上下文中通常是正确的。最后,我仍然可能无法使用这种方法,因为如果我理解正确的话,就不能将其全部放置在SP中。不过谢谢!
crokusek 2012年

2

好吧,我知道这是一个旧线程,我知道我要提出的是一个复杂的hack,但是以防万一它可以帮助任何人,这是可行的:由于记录了这些备份错误,因此可以在捕获中使用xp_readerrorlog块以抓取日志以获取相关消息(错误或信息)。您可以四处搜索xp_readerrorlog参数,但简而言之,您可以指定搜索字符串和开始时间过滤器,在这种情况下非常有用。不知道这是否有助于您重试逻辑,但是为了捕获信息或错误记录,我想出了类似的东西...

IF OBJECT_ID('tempdb.dbo.#Results') IS NOT NULL DROP TABLE #Results
CREATE TABLE #Results (LogDate datetime,ProcessInfo nvarchar(100),LogText nvarchar(4000))
BEGIN TRY
SELECT @begintime = GETDATE()
EXEC sp_executesql @SQL --your backup statement string
INSERT #Results
EXEC  xp_readerrorlog 0, 1, N'backed up',@databasename,@begintime
SELECT @result = LogText from #Results where ProcessInfo = 'Backup' order by logdate desc
END TRY
BEGIN CATCH
INSERT #Results
EXEC  xp_readerrorlog 0, 1, N'Backup',@databasename,@begintime
SELECT @result = LogText from #Results where ProcessInfo = 'spid'+cast(@@SPID as varchar(6)) order by logdate desc
END CATCH
PRINT @result

高温超导


这对于某些常见错误非常有用,但是有些错误显然仅直接引发给客户端。sp_readerrorlog日志将包含一条消息,指出“应用程序日志”,在这里我以“应用程序”为前提,它们表示发出命令的外部进程。 SO Link
crokusek 2015年

0

您可以将错误详细信息记录到表中。您也可以创建一个日志文件,但是可能需要CLR或xp_cmdshell来执行。您也可以发送数据库邮件,但这可能会导致垃圾邮件问题,并且不是正确的日志。

该表是最简单的。

  1. 创建用于存储错误的表
  2. 创建一个插入到错误表中的存储过程
  3. 在catch块中调用存储过程

查看下面链接中提供的Jeremy Kadlec的示例:

http://www.mssqltips.com/sqlservertip/1152/standardized-sql-server-error-handling-and-centralized-logging/


3
问题不在于如何处理错误,而在于内的某些命令没有正确的错误消息CATCH。这是因为仅在以下情况中返回了最后一条错误消息ERROR_MESSAGE()
亚伦·伯特兰
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.