Как записать подробности об ошибках при использовании 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 завершается ненормально.

где выданы необработанные команды:

    backup DATABASE someDb to disk...

Результаты в лучших деталях:

Ошибка поиска - ошибка базы данных SQL Server: возникла неустранимая ошибка ввода-вывода в файле «H: \ FolderName \ Filename.bak:» 112 (на диске недостаточно места).

Есть ли способ перехватить эти детали в переменных внутри хранимой процедуры (для регистрации, передачи обратно вызывающей стороне, для логики повторения)? Кажется, подробности передаются по каналу сообщений, но я бы хотел, чтобы они были доступны в SP.

crokusek
источник
Вы можете захотеть увидеть это: stackoverflow.com/questions/5966670/…
8kb

Ответы:

13

Когда BACKUP DATABASEгенерирует ошибку, на самом деле генерирует два. К сожалению TRY/CATCH, не может захватить первую ошибку; это только захватывает вторую ошибку.

Я подозреваю, что вам лучше всего понять реальную причину неудачного резервного копирования - автоматизировать резервное копирование с помощью SQLCMD-oотправкой вывода в файл), SSIS, C #, PowerShell и т. Д. Все это даст вам гораздо больший контроль над захватом всех из ошибок.

Ответ SO в комментарии предлагает использовать DBCC OUTPUTBUFFER- хотя это возможно, это совсем не похоже на детскую игру. Не стесняйтесь получать удовольствие от этой процедуры с сайта Эрланда Соммарскога , но, похоже, в сочетании с этим она не очень хорошо работает TRY/CATCH.

Единственный способ, которым я, кажется, был в состоянии захватить сообщение об ошибке, spGET_LastErrorMessage- это если фактическая ошибка действительно сброшена. Если вы заключите его в 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
@crokusek Я попробовал один вариант, и результат оказался пустым. Я сделаю еще один снимок сегодня.
Аарон Бертран
В случае отсутствия ошибки LastErrorMessage () получает результаты любой предыдущей ошибки из сеанса? Затем, если два последних exec выполняются как сценарий, возможно, первый exec может быть заключен в try / catch и в catch, установить переменную, а затем перебросить. Тогда LastError вызывается, только если переменная установлена. Предполагается, что повторный бросок не пропускает второй вызов, который, как я думаю, обычно является верным в контексте сценариев. Я все еще не могу использовать этот подход в конце концов, потому что он не может быть все помещен в SP, если я правильно понимаю. Спасибо хоть!
crokusek
2

Ну, я знаю, что это старый поток, и я знаю, что я собираюсь предложить извилистый хак, но на всякий случай, если он может кому-нибудь помочь, вот так: Так как эти ошибки резервного копирования регистрируются, вы можете использовать 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 будет включать в себя сообщение, говорящее о «журнале приложений», где я предполагаю, что под «приложением» они подразумевают внешний процесс, который выдает команды. ТАК Ссылка
crokusek
0

Вы можете записать подробности ошибки в таблицу. Вы также можете создать файл журнала, но для этого может потребоваться CLR или xp_cmdshell. Вы также можете отправлять почту базы данных, но это может вызвать проблемы со спамом и не является правильным журналом.

Таблица самая простая.

  1. Создать таблицу для хранения ошибок
  2. Создайте хранимую процедуру, которая вставит в таблицу ошибок
  3. Вызовите хранимую процедуру в блоке catch

Посмотрите на пример Джереми Кадлека, приведенный в ссылке ниже:

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

Брайан
источник
3
Проблема не в том, что делать с ошибками, а в том, что правильное сообщение об ошибке не доступно для некоторых команд внутри CATCH. Это потому, что только последнее сообщение об ошибке возвращается в ERROR_MESSAGE()...
Аарон Бертран