Трансакція у збереженій процедурі


12

Мені потрібно виконати ОНОВЛЕННЯ та ВСТУП в одній транзакції. Цей код добре працює сам по собі, але я хотів би мати можливість його легко зателефонувати і передати необхідні параметри. Коли я намагаюся вкласти цю транзакцію в збережену процедуру, я стикаюся з безліччю синтаксичних помилок.

Як я можу інкапсулювати наступний код, щоб його можна було легко викликати?

BEGIN TRANSACTION AssignUserToTicket
GO

DECLARE @updateAuthor varchar(100)
DECLARE @assignedUser varchar(100)
DECLARE @ticketID bigint

SET @updateAuthor = 'user1'
SET @assignedUser = 'user2'
SET @ticketID = 123456

    UPDATE tblTicket SET ticketAssignedUserSamAccountName = @assignedUser WHERE (ticketID = @ticketID);
    INSERT INTO [dbo].[tblTicketUpdate]
           ([ticketID]
           ,[updateDetail]
           ,[updateDateTime]
           ,[userSamAccountName]
           ,[activity])
     VALUES
           (@ticketID,
           'Assigned ticket to ' + @assignedUser,
           GetDate(),
           @updateAuthor,
           'Assign');
GO
COMMIT TRANSACTION AssignUserToTicket

1
Ймовірно, це допоможе, якщо ви додасте детальну інформацію у своє запитання про те, що саме є "помилками" (скористайтеся посиланням редагування під вашим запитанням). Також, будь ласка, взяти тур . Спасибі!
Макс Вернон,

Відповіді:


15

Вам подобається загортати цей код у CREATE PROCEDURE ...синтаксис та видаляти GOзаяви після BEGIN TRANSACTIONта до COMMIT TRANSACTION.

GO
CREATE PROCEDURE dbo.AssignUserToTicket
(
     @updateAuthor varchar(100)
    , @assignedUser varchar(100)
    , @ticketID bigint
)
AS
BEGIN
    BEGIN TRANSACTION;
    SAVE TRANSACTION MySavePoint;
    SET @updateAuthor = 'user1';
    SET @assignedUser = 'user2';
    SET @ticketID = 123456;

    BEGIN TRY
        UPDATE dbo.tblTicket 
        SET ticketAssignedUserSamAccountName = @assignedUser 
        WHERE (ticketID = @ticketID);

        INSERT INTO [dbo].[tblTicketUpdate]
            (
            [ticketID]
            ,[updateDetail]
            ,[updateDateTime]
            ,[userSamAccountName]
            ,[activity]
            )
        VALUES (
            @ticketID
            , 'Assigned ticket to ' + @assignedUser
            , GetDate()
            , @updateAuthor
            , 'Assign'
            );
        COMMIT TRANSACTION 
    END TRY
    BEGIN CATCH
        IF @@TRANCOUNT > 0
        BEGIN
            ROLLBACK TRANSACTION MySavePoint; -- rollback to MySavePoint
        END
    END CATCH
END;
GO

Також зауважте, я додав TRY...CATCHблок операторів, щоб дозволити виконувати ROLLBACK TRANSACTIONоператор у випадку виникнення помилки. Вам, мабуть, потрібні кращі помилки, ніж це, але без знання ваших вимог, це в кращому випадку складно.

Деякі хороші читання:

  1. Завжди вказуйте схему

  2. Найкращі практики, що зберігаються

  3. Шкідливих звичок уникати


1
Ви все ще хочете мати збережену транзакцію. Якщо ви вкладете транзакцію в ІП, і ІП загорнеться в іншу транзакцію, матеріал провалиться. sqlstudies.com/2014/01/06/…
Кеннет Фішер

дякую, що вказали на мене. Я знаю, що в SQL Server немає вкладених транзакцій, проте я не знав про SAVE TRANSнаслідки команди.
Макс Вернон,

8

Якщо ви хочете правильно обробити вкладені збережені процедури, які можуть обробляти транзакції (починаючи з T-SQL чи коду програми), слід дотримуватися шаблону, який я описав у наступній відповіді:

Чи потрібно нам обробляти транзакції в коді C #, а також у збереженій процедурі

Ви помітите дві відмінності від того, що ви робите тут:

  1. Використання RAISERRORвсередині CATCHблоку. Це додає помилку до рівня виклику (чи то в рівні DB, чи у програмі), тому можна прийняти рішення щодо факту помилки.

  2. Ні SAVE TRANSACTION. Я ніколи не знаходив випадку для цього. Я знаю, що деякі люди вважають за краще це, але в усьому, що я коли-небудь робив у будь-якому місці, в якому я працював, поняття помилки, що виникає на будь-якому з вкладених рівнів, означало, що будь-яка робота, яка вже була зроблена, недійсна. Використовуючи, SAVE TRANSACTIONви повертаєтесь до стану лише перед тим, як викликати цю Збережену процедуру, залишаючи існуючий процес як інакше дійсний.

    Якщо ви хочете отримати докладніші відомості SAVE TRANSACTION, перегляньте інформацію у цій відповіді:

    Як відкати, коли з однієї збереженої процедури запускаються 3 збережені процедури

    Іншим питанням SAVE TRANSACTIONє нюанс його поведінки, як зазначено на сторінці MSDN для ЗБЕРЕГО ТРАНЗАКЦІЇ (акцент додано):

    Повторювані імена точок збереження дозволено в транзакції, але оператор ROLLBACK TRANSACTION, який вказує ім'я точки збереження, поверне транзакцію лише до останньої SAVE TRANSACTION, використовуючи це ім'я.

    Це означає, що вам потрібно бути дуже обережним, щоб кожній точці збереження у кожній збереженій процедурі дати ім'я, яке є унікальним для всіх точок збереження у всіх збережених процедурах. Наступні приклади ілюструють цей момент.

    Цей перший приклад показує, що відбувається при повторному використанні імені Save Point; повертається лише нижня точка збереження.

    IF (OBJECT_ID(N'tempdb..#SaveTranTestA') IS NOT NULL)
    BEGIN
        DROP TABLE #SaveTranTestA;
    END;
    CREATE TABLE #SaveTranTestA (SomeVal INT NOT NULL);
    
    BEGIN TRAN; -- start level 1
    SAVE TRANSACTION MySavePoint;
    
    SELECT @@TRANCOUNT AS [TranCount]; -- 1
    
    INSERT INTO #SaveTranTestA (SomeVal) VALUES (100);
    
    BEGIN TRAN; -- start level 2
    SAVE TRANSACTION MySavePoint;
    
    SELECT @@TRANCOUNT AS [TranCount]; -- 2
    
    INSERT INTO #SaveTranTestA (SomeVal) VALUES (200);
    
    COMMIT; -- exit level 2
    
    SELECT @@TRANCOUNT AS [TranCount]; -- 1
    SELECT * FROM #SaveTranTestA;
    -- 100
    -- 200
    
    ROLLBACK TRANSACTION MySavePoint; -- error occurred; undo actions up to this point
    
    SELECT @@TRANCOUNT AS [TranCount]; -- 1
    SELECT * FROM #SaveTranTestA;
    -- 100
    
    COMMIT; -- exit level 1
    
    SELECT @@TRANCOUNT AS [TranCount]; -- 0
    SELECT * FROM #SaveTranTestA;
    -- 100

    Цей другий приклад показує, що відбувається, коли ви використовуєте унікальні імена Save Point; бажану точку збереження рівня відкочують назад.

    IF (OBJECT_ID(N'tempdb..#SaveTranTestB') IS NOT NULL)
    BEGIN
        DROP TABLE #SaveTranTestB;
    END;
    CREATE TABLE #SaveTranTestB (SomeVal INT NOT NULL);
    
    BEGIN TRAN; -- start level 1
    SAVE TRANSACTION MySavePointUno;
    
    SELECT @@TRANCOUNT AS [TranCount]; -- 1
    
    INSERT INTO #SaveTranTestB (SomeVal) VALUES (100);
    
    BEGIN TRAN; -- start level 2
    SAVE TRANSACTION MySavePointDos;
    
    SELECT @@TRANCOUNT AS [TranCount]; -- 2
    
    INSERT INTO #SaveTranTestB (SomeVal) VALUES (200);
    
    COMMIT; -- exit level 2
    
    SELECT @@TRANCOUNT AS [TranCount]; -- 1
    SELECT * FROM #SaveTranTestB;
    -- 100
    -- 200
    
    ROLLBACK TRANSACTION MySavePointUno; --error occurred; undo actions up to this point
    
    SELECT @@TRANCOUNT AS [TranCount]; -- 1
    SELECT * FROM #SaveTranTestB;
    -- <no rows>
    
    COMMIT; -- exit level 1
    
    SELECT @@TRANCOUNT AS [TranCount]; -- 0
    SELECT * FROM #SaveTranTestB;
    -- <no rows>

Ось чому я використовую @@ NESTLEVEL для створення мого імені SAVE TRANSACTION збереження.
Вінсент Ванкальберг
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.