Как БРОСИТЬ исключение во внутренней транзакции, не заканчивая выполнение хранимой процедуры SQL Server? - PullRequest
0 голосов
/ 12 февраля 2019

Моя цель - выдать исключение обратно вызывающей стороне, но продолжить выполнение хранимой процедуры SQL Server.Так что, по сути, я пытаюсь выполнить try..catch..finally block, хотя, насколько мне известно, SQL Server не имеет понятия блока try..catch..finally.

У меня есть примерstored procedure для иллюстрации.Это просто пример, который я придумал, так что, пожалуйста, не обращайте слишком много внимания на схему таблицы.Надеюсь, вы понимаете суть того, что я пытаюсь осуществить здесь.В любом случае, сохраненный процесс содержит explicit transaction, который выбрасывает exception в пределах catch block.Дальнейшее выполнение после блока try..catch не выполняется, если выполняется THROW.Насколько я понимаю, по крайней мере в SQL Server THROW не может отличить внутренние и внешние транзакции или вложенные транзакции.

В этой хранимой процедуре у меня есть две таблицы: Tbl1 и Tbl2 . Tbl1 имеет primary key на Tbl1.ID . Tbl2 имеет foreign key на EmpFK , который сопоставляется с Tbl1.ID . EmpID имеет уникальное ограничение.Повторные записи не могут быть вставлены в Tbl1 Tbl1 и Tbl2 имеют первичный ключ на ID и используют приращение идентификатора для автоматической вставки.Хранимая процедура имеет три входных параметра, один из которых employeeID .

Внутри внутренней транзакции запись вставляется в Tbl1 - новый идентификатор сотрудникадобавлено.В случае неудачи идея заключается в том, чтобы транзакция изящно выдавала ошибку, но сохраненный процесс должен продолжать работать до завершения.Независимо от того, была ли вставка таблицы успешной или неудачной, позже будет использоваться EmpID для заполнения EmpFk .

После блока try..catch я выполняю поиск Tbl1.ID через параметр employeeID , который передается в хранимый процесс.Затем я вставляю запись в TBl2; Tbl1.ID - это значение для Tbl2.EmpFK .

(И вы можете спросить: «Зачем использовать такую ​​схему? Почему бы не объединить в одну таблицу с такимималенький набор данных? »Опять же, это всего лишь пример. Это не обязательно должны быть сотрудники. Вы можете выбрать что угодно. Это всего лишь виджет. Представьте, что Tbl1 может содержать очень, очень большой набор данных.В камне есть две таблицы, которые имеют отношение первичный ключ / внешний ключ.)

Вот пример набора данных:

Tbl1
ID EmpID
1  AAA123
2  AAB123
3  AAC123

Tbl2
ID Role        Location EmpFK
1  Junior      NW       1
2  Senior      NW       2
3  Manager     NE       2
4  Sr Manager  SE       3
5  Director    SW       3

Вот пример хранимой процедуры:

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

CREATE PROCEDURE [dbo].[usp_TestProc]

    @employeeID VARCHAR(10)
    ,@role VARCHAR(50)
    ,@location VARCHAR(50)

AS
BEGIN

    SET NOCOUNT ON;

    DECLARE @employeeFK INT;

    BEGIN TRY
        BEGIN TRANSACTION MYTRAN;

            INSERT [Tbl1] (
                [EmpID]
            )
            VALUES (
                @employeeID
            );

        COMMIT TRANSACTION MYTRAN;
    END TRY

    BEGIN CATCH

        IF @@TRANCOUNT > 0
        BEGIN

            ROLLBACK TRANSACTION MYTRAN;

        END;

        THROW; -- Raises exception, exiting stored procedure

    END CATCH;

    SELECT
        @employeeFK = [ID]
    FROM
        [Tbl1]
    WHERE
        [EmpID] = @employeeID;

    INSERT [Tbl2] (
        [Role]
        ,[Location]
        ,[EmpFK]
    )
    VALUES (
        @role
        ,@location
        ,@employeeFK
    );

END;

Итак, еще раз, я все еще хочу вернуть ошибку вызывающей стороне, то есть зарегистрировать ошибку, но я не хочу, чтобы она остановила выполнение хранимой процедуры в холодном состоянии.Это должно продолжаться очень похоже на блок try..catch..finally.Можно ли это сделать с помощью THROW или я должен использовать альтернативные средства?

Может быть, я ошибаюсь, но не является THROW обновленной версией RAISERROR, и, в дальнейшем, мы должны использовать первоедля обработки исключений?

Я использовал RAISERROR в прошлом для этих ситуаций, и мне это подходит.Но THROW - это более простое, элегантное решение, имхо, и, возможно, будет лучшей практикой в ​​будущем.Я не совсем уверен.

Спасибо за вашу помощь заранее.

1 Ответ

0 голосов
/ 12 февраля 2019

В камне есть две таблицы, которые имеют отношение первичный ключ / внешний ключ.

Использование THROW во внутренней транзакции - это не способ сделать то, что вы хотите.Судя по вашему коду, вы хотите вставить нового сотрудника, если этот сотрудник уже существует, а затем, независимо от того, существовал ли сотрудник уже или нет, вы хотите использовать PK / id этого сотрудника во второй вставке в дочернюю таблицу.

Один из способов сделать это - разделить логику.Это psuedocode для того, что я имею в виду:

IF NOT EXISTS(Select employee with @employeeId)
  INSERT the new employee

SELECT @employeeFK like you are doing.

INSERT into Table2 like you are doing.

Если вам все еще нужно вызвать ошибку, когда передается уже существующий @employeeId, вы можете поставить ELSE после IF и заполнить строковую переменную,и в конце процедуры, если переменная была заполнена, то выдается / поднимается ошибка.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...