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

Использование THROW для обработки ошибок в SQL Server
Раздел: Функции обработки исключений, Обработка ошибок
THROW(error_number, message, state): N/A

Функция THROW в MS SQL Server

Функция THROW используется для генерации пользовательских исключений и передачи управления блоку CATCH в конструкции TRY...CATCH. Эта функция доступна начиная с SQL Server 2012.

Функция применяется в ситуациях, когда требуется прервать выполнение пакета или процедуры из-за нарушенного бизнес-правила, недопустимого состояния данных или других ошибок логики приложения. Её вызов приводит к возврату управления в блок CATCH или завершению выполнения, если блока CATCH нет.

Синтаксис:

THROW [ { error_number | @local_variable },
{ message | @local_variable },
{ state | @local_variable }
] [ ; ]

Аргументы:

  • error_number — целочисленная константа или переменная, представляющая номер исключения. Допустимый диапазон от 50000 до 2147483647. Номера, меньшие 50000, зарезервированы системой SQL Server.
  • message — строка nvarchar(2048), описывающая причину исключения.
  • state — tinyint (от 0 до 255) или переменная, указывающая состояние ошибки. Значение 0 по умолчанию.

Возвращаемое значение: Функция THROW не возвращает значение. Её выполнение всегда приводит к разрыву потока выполнения.

Особенности: Если параметр error_number равен NULL, используется номер ошибки 50000. Если message равен NULL, используется текст последней ошибки, сгенерированной в сеансе. Если блок CATCH отсутствует, сеанс прерывается.

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

Базовый вызов с указанием всех параметров:

THROW 51000, 'Нарушено бизнес-правило: сумма не может быть отрицательной.', 1;
Сообщение 51000, уровень 16, состояние 1, строка 1
Нарушено бизнес-правило: сумма не может быть отрицательной.

Использование переменных для передачи параметров:

DECLARE @ErrorNumber INT = 50001,
@ErrorMessage NVARCHAR(2048) = N'Ошибка валидации данных',
@ErrorState TINYINT = 10;
THROW @ErrorNumber, @ErrorMessage, @ErrorState;
Сообщение 50001, уровень 16, состояние 10, строка 4
Ошибка валидации данных.

Повторное возбуждение исходной ошибки внутри блока CATCH без параметров:

BEGIN TRY
SELECT 1/0;
END TRY
BEGIN CATCH
PRINT 'Произошла ошибка, повторно возбуждаю её.';
THROW;
END CATCH
Произошла ошибка, повторно возбуждаю её.
Сообщение 8134, уровень 16, состояние 1, строка 2
Обнаружено деление на ноль.

Похожие функции в MS SQL Server

RAISERROR — устаревшая функция для генерации пользовательских сообщений об ошибках. Поддерживает форматирование через аргументы, подобное функции printf. В отличие от THROW, RAISERROR может генерировать ошибки с уровнем серьёзности от 0 до 25, но не всегда прерывает выполнение пакета. Для новых разработок рекомендуется использовать THROW.

TRY...CATCH — не функция, а конструкция языка для обработки исключений. THROW обычно используется внутри неё для явного возбуждения ошибок. Конструкция обеспечивает перехват и обработку исключений.

Альтернативы в других системах

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

SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Пользовательская ошибка';
ERROR 1644 (45000): Пользовательская ошибка

PostgreSQL: Применяется команда RAISE с указанием уровня (DEBUG, LOG, INFO, NOTICE, WARNING, EXCEPTION).

RAISE EXCEPTION 'Ошибка проверки %', 'данных' USING ERRCODE = 'P0001';
ERROR: Ошибка проверки данных

Oracle: Используется RAISE_APPLICATION_ERROR в составе PL/SQL. Позволяет задать номер ошибки (от -20000 до -20999) и сообщение.

RAISE_APPLICATION_ERROR(-20001, 'Недопустимое значение параметра');
ORA-20001: Недопустимое значение параметра

Типичные ошибки при работе с THROW

Вызов THROW без параметров вне блока CATCH приводит к ошибке.

THROW;
Сообщение 10704, уровень 15, состояние 1, строка 1
Для использования THROW без параметров инструкция должна находиться внутри блока CATCH.

Использование номера ошибки вне допустимого диапазона.

THROW 49999, 'Тест', 1;
Сообщение 35100, уровень 16, состояние 10, строка 1
Номер ошибки в инструкции THROW должен быть больше или равен 50000 и меньше или равен 2147483647.

Пропуск точки с запятой перед инструкцией THROW в некоторых контекстах может вызвать синтаксическую ошибку.

BEGIN
PRINT 'Test'
THROW 50000, 'Error', 1
END
Сообщение 102, уровень 15, состояние 1, строка 3
Синтаксическая ошибка около 'THROW'.

Изменения в последних версиях

В SQL Server 2017 и более новых версиях не было внесено существенных изменений в синтаксис или поведение функции THROW. Основные возможности были представлены в версии 2012, где функция появилась. Рекомендуется следить за официальной документацией на случай появления новых возможностей.

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

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

Пример sql
CREATE PROCEDURE UpdateBalance @AccountId INT, @Delta DECIMAL(10,2)
AS
BEGIN
IF @Delta < 0
THROW 51000, 'Изменение баланса не может быть отрицательным', 1;
-- Основная логика
PRINT 'Баланс обновлён.';
END

BEGIN TRY
EXEC UpdateBalance 1, -100;
END TRY
BEGIN CATCH
PRINT 'Ошибка: ' + ERROR_MESSAGE();
-- Логирование или откат транзакции
END CATCH
Ошибка: Изменение баланса не может быть отрицательным

Каскадное возбуждение ошибок с разными состояниями для отладки сложных сценариев.

Пример sql
BEGIN TRY
BEGIN TRY
THROW 50001, 'Внутренняя ошибка', 5;
END TRY
BEGIN CATCH
PRINT 'Внутренний блок перехватил ошибку с состоянием: ' + CAST(ERROR_STATE() AS NVARCHAR);
THROW 50002, 'Внешняя ошибка после обработки', 10;
END CATCH
END TRY
BEGIN CATCH
PRINT 'Внешний блок перехватил ошибку с состоянием: ' + CAST(ERROR_STATE() AS NVARCHAR);
PRINT 'Сообщение: ' + ERROR_MESSAGE();
END CATCH
Внутренний блок перехватил ошибку с состоянием: 5
Внешний блок перехватил ошибку с состоянием: 10
Сообщение: Внешняя ошибка после обработки

Комбинирование THROW и XACT_STATE для управления транзакциями в распределённых сценариях.

Пример sql
BEGIN TRY
BEGIN TRAN;
-- Некоторая операция
IF @@TRANCOUNT > 0 AND XACT_STATE() = 1
ROLLBACK TRAN;
THROW 60000, 'Ошибка с откатом транзакции', 1;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 AND XACT_STATE() = 1
ROLLBACK TRAN;
THROW;
END CATCH
Сообщение 60000, уровень 16, состояние 1, строка 8
Ошибка с откатом транзакции

MS SQL THROW function comments

En
THROW Raises an exception and transfers execution to a CATCH block