Как можно вызывать пользовательские ошибки из хранимой процедуры?sql-54

В SQL Server существует несколько способов вызова пользовательских ошибок из хранимых процедур, каждый со своими особенностями и вариантами использования.

1. Инструкция RAISERROR

CREATE PROCEDURE dbo.ValidateOrder
    @OrderID INT
AS
BEGIN
    IF NOT EXISTS (SELECT 1 FROM Orders WHERE OrderID = @OrderID)
    BEGIN
        RAISERROR('Order with ID %d does not exist', 16, 1, @OrderID)
        RETURN -1
    END
    -- Основная логика процедуры
END
  • Параметры:

    • Сообщение: Может содержать параметры форматирования (%d, %s и т.д.)
    • Severity (16): Уровень серьезности (11-19 для пользовательских ошибок)
    • State (1): Состояние ошибки (0-255)
    • Доп. параметры: Значения для подстановки в сообщение
  • Особенности:

    • Поддерживает старый стиль форматирования
    • Не вызывает прерывание транзакции по умолчанию

2. Инструкция THROW

CREATE PROCEDURE dbo.ProcessPayment
    @Amount DECIMAL(18,2)
AS
BEGIN
    IF @Amount <= 0
    BEGIN
        THROW 50001, 'Payment amount must be positive', 1;
    END

    -- Основная логика процедуры
END
  • Параметры:

    • Error Number: Должен быть >= 50000 для пользовательских ошибок
    • Message: Текст сообщения об ошибке
    • State: Состояние ошибки (0-255)
  • Преимущества:

    • Более современный синтаксис (SQL Server 2012+)
    • Всегда прерывает выполнение пакета с уровнем серьезности 16
    • Не требует предварительного объявления ошибки

3. Предварительное объявление ошибок

-- Добавление пользовательского сообщения
EXEC sp_addmessage
    @msgnum = 50002,
    @severity = 16,
    @msgtext = 'Invalid customer status: %s',
    @lang = 'us_english';

-- Использование в процедуре
CREATE PROCEDURE dbo.ValidateCustomer
    @Status VARCHAR(20)
AS
BEGIN
    IF @Status NOT IN ('Active', 'Pending', 'Inactive')
    BEGIN
        RAISERROR(50002, 16, 1, @Status)
        RETURN
    END
END
  • Применение:
    • Для стандартизированных сообщений в приложении
    • Когда нужна поддержка нескольких языков
    • Для ошибок, используемых в нескольких процедурах

4. Возврат кодов ошибок через RETURN

CREATE PROCEDURE dbo.CheckInventory
    @ProductID INT
AS
BEGIN
    IF NOT EXISTS (SELECT 1 FROM Products WHERE ProductID = @ProductID)
        RETURN 1 -- Код ошибки

    DECLARE @Qty INT = (SELECT Quantity FROM Inventory WHERE ProductID = @ProductID)

    IF @Qty <= 0
        RETURN 2 -- Другой код ошибки

    -- Успешное выполнение
    RETURN 0
END
  • Особенности:
    • Может возвращать только целые числа (0 обычно означает успех)
    • Требует дополнительной обработки на стороне клиента
    • Не прерывает выполнение процедуры

5. Выбор уровня серьезности

УровеньЗначениеПоведение
0-10Информационные сообщенияНе прерывает выполнение
11-16Пользовательские ошибкиПрерывает пакет (для THROW)
17-19Серьезные системные ошибкиПрерывает соединение
20+Фатальные ошибки системыПрерывает процесс SQL Server

6. Логирование ошибок

CREATE PROCEDURE dbo.ProcessOrder
    @OrderID INT
AS
BEGIN
    BEGIN TRY
        -- Логика обработки заказа
    END TRY
    BEGIN CATCH
        INSERT INTO ErrorLog(ErrorNumber, ErrorMessage, ProcedureName)
        VALUES (ERROR_NUMBER(), ERROR_MESSAGE(), 'ProcessOrder')

        THROW; -- Повторно вызываем ошибку
    END CATCH
END
  • Рекомендации:
    • Всегда логируйте ошибки для последующего анализа
    • Используйте TRY/CATCH для обработки ошибок
    • THROW без параметров повторяет текущую ошибку

7. Пользовательские исключения с XACT_ABORT

CREATE PROCEDURE dbo.TransferFunds
    @FromAccount INT,
    @ToAccount INT,
    @Amount DECIMAL(18,2)
AS
BEGIN
    SET XACT_ABORT ON; -- Автоматический ROLLBACK при ошибке

    BEGIN TRANSACTION;
        -- Логика перевода
        IF @Amount <= 0
            THROW 50003, 'Amount must be positive', 1;

        COMMIT;
END
  • XACT_ABORT ON:
    • Обеспечивает автоматический откат транзакции при ошибке
    • Рекомендуется для процедур с транзакциями

Резюмируем: Для вызова пользовательских ошибок в SQL Server предпочтительно использовать THROW для простых случаев и sp_addmessage для стандартизированных сообщений. Всегда учитывайте уровень серьезности ошибки и её влияние на транзакции. Для комплексной обработки ошибок сочетайте TRY/CATCH блоки с логированием и соответствующими кодами возврата.