В хранимых процедурах можно и нужно обрабатывать ошибки с помощью специальных конструкций.
В SQL Server главные механизмы — блоки TRY...CATCH, где все ошибки внутри TRY отлавливаются, и в CATCH можно зафиксировать детали. Там доступны функции вроде ERROR_NUMBER(), ERROR_MESSAGE() для получения деталей.
BEGIN TRY -- Рискованная операция UPDATE Accounts SET balance = balance - 100 WHERE id = 1; END TRY BEGIN CATCH INSERT INTO ErrorLog( ErrorTime, ErrorNumber, ErrorMessage, UserName ) VALUES ( GETDATE(), ERROR_NUMBER(), ERROR_MESSAGE(), SUSER_SNAME() ); -- Дополнительное восстановление или ROLLBACK END CATCH
В Oracle чаще используются EXCEPTION-блоки:
BEGIN UPDATE ...; EXCEPTION WHEN OTHERS THEN INSERT INTO error_log VALUES (..., SQLERRM); END;
Моменты, о которых важно помнить:
Может ли исключение в блоке CATCH привести к потере контекста ошибки? Каким образом реализовать вложенную обработку ошибок?
Ответ и пример: Если в CATCH-блоке произойдет ошибка (например, из-за недоступности таблицы ErrorLog), то оригинальный контекст ошибки теряется, и информация о причине сбоя может быть утеряна.
Чтобы предохраниться, инкапсулируйте логирование в отдельную процедуру с собственным TRY...CATCH, чтобы всегда ловить "ошибку в обработчике ошибок".
BEGIN TRY -- main code END TRY BEGIN CATCH EXEC LogError @Error = ERROR_MESSAGE(); END CATCH -- Процедура LogError сама содержит свой TRY...CATCH
История
Проект: Финансовая отчетность. В хранимых процедурах добавили блоки TRY...CATCH, но не залогировали параметры, с которыми происходила ошибка. В результате при отлове критических сбоев приходилось вручную искать ситуацию из бэкапа — root cause был неочевиден.
История
Проект: Автоматизация документооборота (Oracle). В EXCEPTION-блоке забыли логировать имя пользователя. Через неделю расследования обнаружили, что кто-то умышленно "ломал" документы — выяснили только по косвенным признакам в журнале аудита.
История
Проект: E-commerce. Процедура при сбое писала ошибку в ErrorLog. Однажды таблица Log была заблокирована разросшейся транзакцией, и попытка логирования привела к вложенной ошибке, которая перезаписала оригинальную причину и очистила стек ошибок. Исправили внедрением дополнительной таблицы для критических сбоев и многоуровневого логирования.