RAISERROR: примеры (SQL)

RAISERROR в MS SQL: генерирование пользовательских ошибок
Раздел: Функции обработки исключений, Обработка ошибок
RAISERROR: int

Функция RAISERROR в MS SQL

Функция RAISERROR в Microsoft SQL Server предназначена для генерации пользовательских сообщений об ошибках и передачи управления обработчику ошибок. Использование функции происходит в сценариях валидации данных, обработки бизнес-логики или для сигнализации о состоянии выполнения пакетов и процедур.

Синтаксис функции включает следующие аргументы:

RAISERROR ( { msg_id | msg_str | @local_variable }
{ , severity , state }
[ , argument [ , ...n ] ] )
[ WITH option [ , ...n ] ]
  • msg_id: Уникальный идентификатор пользовательской ошибки, зарегистрированный в системном представлении sys.messages. Должен быть больше или равен 50000.
  • msg_str: Строка сообщения, схожая с функцией printf в языке C. Может содержать заполнители для аргументов.
  • @local_variable: Переменная, содержащая строку сообщения.
  • severity: Уровень серьезности ошибки от 0 до 25. Пользовательские уровни обычно находятся в диапазоне 11-20. Уровни 20-25 считаются фатальными и требуют указания параметра WITH LOG.
  • state: Целое число от 1 до 255, обозначающее состояние ошибки. Используется для идентификации места возникновения ошибки в коде.
  • argument: Параметры, подставляемые в заполнители строки сообщения. Допускается до 20 аргументов.
  • WITH option: Дополнительные параметры:
    • WITH LOG — запись ошибки в журнал приложения и журнал ошибок SQL Server.
    • WITH NOWAIT — немедленная отправка сообщения клиенту.
    • WITH SETERROR — установка значения @@ERROR равным указанному msg_id или 50000.

Функция не возвращает значения в традиционном понимании, но влияет на системные функции @@ERROR и вызывает выполнение блока CATCH при использовании в конструкции TRY...CATCH.

Простые примеры использования

Пример с пользовательским текстом сообщения и уровнем серьезности 16:

RAISERROR ('Нарушение бизнес-правила', 16, 1);
Сообщение 50000, уровень 16, состояние 1, строка 1
Нарушение бизнес-правила

Использование форматирования строки с аргументами:

DECLARE @ProductName NVARCHAR(50) = N'Молоко';
DECLARE @MinQuantity INT = 10;
RAISERROR (N'Товар \"%s\" остался в количестве меньше %d единиц', 10, 1, @ProductName, @MinQuantity);
Сообщение 50000, уровень 10, состояние 1, строка 3
Товар "Молоко" остался в количестве меньше 10 единиц

Использование зарегистрированного пользовательского сообщения:

EXEC sp_addmessage @msgnum = 50005, @severity = 16,
@msgtext = N'Недействительное значение параметра: %s', @lang = 'русский';
RAISERROR (50005, 16, 1, N'ID пользователя');
Сообщение 50005, уровень 16, состояние 1, строка 2
Недействительное значение параметра: ID пользователя

Пример с параметром WITH LOG:

RAISERROR ('Критическая ошибка конфигурации', 22, 1) WITH LOG;
Сообщение 50000, уровень 22, состояние 1, строка 1
Критическая ошибка конфигурации
// Сообщение также записывается в журнал ошибок SQL Server

Альтернативные механизмы в MS SQL

THROW — инструкция, появившаяся в SQL Server 2012. Она имеет более простой синтаксис: THROW [ error_number, message, state ]. В отличие от RAISERROR, THROW всегда устанавливает уровень серьезности 16. Инструкция THROW требует указания существующего номера ошибки или 50000. THROW может быть использована без параметров для повторного вызова пойманной ошибки в блоке CATCH.

PRINT — инструкция для вывода информационных сообщений клиенту с уровнем серьезности 0. Она не вызывает ошибку и не передает управление блоку CATCH.

Выбор между RAISERROR и THROW зависит от задачи. THROW предпочтительнее для современных проектов из-за соответствия стандарту SQL и более простого синтаксиса. RAISERROR предоставляет больше контроля над уровнем серьезности и возможностями форматирования, а также поддерживает отправку сообщений в журнал событий.

Аналоги в других СУБД и языках

MySQL: Используется инструкция SIGNAL. Она позволяет указывать SQLSTATE, значение ошибки и сообщение.

SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Пользовательская ошибка';
# Возникает ошибка с заданным SQLSTATE и текстом

Oracle: Применяется процедура RAISE_APPLICATION_ERROR в пакете DBMS_STANDARD.

RAISE_APPLICATION_ERROR(-20001, 'Недействительная операция');
ORA-20001: Недействительная операция

PostgreSQL: Для вызова исключений используются инструкции RAISE EXCEPTION или RAISE.

RAISE EXCEPTION 'Ошибка данных: %', 'неверный формат' USING HINT = 'Проверьте ввод';
ERROR:  Ошибка данных: неверный формат
HINT: Проверьте ввод

SQLite: Нет встроенной функции для пользовательских ошибок. Обычно используется возврат кодов ошибок или вызов исключений на уровне приложения.

Sybase ASE: Поддерживает функцию RAISERROR с синтаксисом, очень похожим на MS SQL.

Языки программирования: В большинстве языков используются конструкции throw new Exception() (C#, Java) или raise (Python). Они, как правило, интегрированы в объектно-ориентированную модель исключений, в отличие от SQL-подхода.

Частые ошибки при применении

Использование несуществующего номера сообщения без предварительной регистрации через sp_addmessage приводит к ошибке.

RAISERROR (60000, 16, 1);
Сообщение 18054, уровень 16, состояние 1, строка 1
Ошибка 60000, уровень 16, состояние 1 не определена. Ошибку следует определять, начиная с 50000.

Указание уровня серьезности 20-25 без параметра WITH LOG вызывает ошибку.

RAISERROR ('Фатальная ошибка', 21, 1);
Сообщение 2745, уровень 16, состояние 2, строка 1
Для ошибок уровня серьезности 20 и выше параметр WITH LOG является обязательным.

Передача аргументов, не соответствующих заполнителям в строке, может приводить к некорректному выводу или ошибке.

RAISERROR ('Значение: %s, %d', 10, 1, 'Текст');
-- Передан только один аргумент вместо двух
Сообщение 50000, уровень 10, состояние 1, строка 1
Значение: Текст, 0

Обновления в новых версиях

Начиная с SQL Server 2012, корпорация Майкрософт рекомендует использовать инструкцию THROW для новой разработки. Функция RAISERROR сохраняется для обратной совместимости, но не получает значительных функциональных обновлений.

Важное изменение связано с уровнем серьезности. В SQL Server 2014 и выше, при использовании RAISERROR с уровнем серьезности от 20 до 25, сеанс завершается, если ошибка не обрабатывается в блоке TRY...CATCH. Это отличает поведение от более старых версий.

В SQL Server 2016 улучшена интеграция RAISERROR с компонентами Always On и группы доступности, но синтаксис и основные возможности остаются неизменными.

Расширенные сценарии применения

Динамическое формирование сообщения об ошибке на основе данных таблицы:

DECLARE @EmpID INT = 999;
DECLARE @ErrorMsg NVARCHAR(4000);
IF NOT EXISTS (SELECT 1 FROM Employees WHERE EmployeeID = @EmpID)
BEGIN
SET @ErrorMsg = N'Сотрудник с ID ' + CAST(@EmpID AS NVARCHAR) + N' не найден.';
RAISERROR (@ErrorMsg, 16, 1);
END
Сообщение 50000, уровень 16, состояние 1, строка 6
Сотрудник с ID 999 не найден.

Использование RAISERROR в блоке TRY...CATCH с последующей обработкой:

BEGIN TRY
RAISERROR ('Тестовая ошибка', 16, 1);
END TRY
BEGIN CATCH
PRINT 'Поймана ошибка: ' + ERROR_MESSAGE();
-- Возможно логирование или другие действия
END CATCH
Поймана ошибка: Тестовая ошибка

Пакетная обработка с накоплением ошибок и выводом итогового сообщения:

DECLARE @ErrorCount INT = 0;
-- ... выполнение операций в цикле
IF @@ERROR <> 0 SET @ErrorCount = @ErrorCount + 1;
-- ...
IF @ErrorCount > 0
RAISERROR ('Завершено с %d ошибками', 10, 1, @ErrorCount);
Сообщение 50000, уровень 10, состояние 1, строка 6
Завершено с 3 ошибками

Использование состояния (state) для идентификации места ошибки в сложной процедуре:

CREATE PROCEDURE sp_ComplexProc
AS
BEGIN
-- Блок 1
IF (1=0) -- Условие имитации ошибки
RAISERROR ('Ошибка в блоке валидации', 16, 1);
-- Блок 2
IF (1=0)
RAISERROR ('Ошибка в блоке расчета', 16, 2);
END
-- В зависимости от того, где возникла ошибка, state будет 1 или 2, что упрощает отладку.

MS SQL RAISERROR function comments

En
RAISERROR Generates an error message and initiates error processing