Giao dịch trong một thủ tục lưu trữ


12

Tôi cần thực hiện CẬP NHẬT và CHỨNG MINH trong một giao dịch. Mã đó tự hoạt động tốt, nhưng tôi muốn có thể gọi nó dễ dàng và chuyển các tham số cần thiết. Khi tôi cố gắng lồng giao dịch này trong một thủ tục được lưu trữ, tôi gặp rất nhiều lỗi cú pháp.

Làm thế nào tôi có thể đóng gói mã sau đây để có thể dễ dàng gọi nó?

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
Sẽ có ích nếu bạn thêm chi tiết vào câu hỏi của mình về chính xác "lỗi" là gì (sử dụng liên kết chỉnh sửa bên dưới câu hỏi của bạn). Ngoài ra, xin vui lòng đi tour . Cảm ơn!
Max Vernon

Câu trả lời:


15

Bạn muốn bọc mã theo CREATE PROCEDURE ...cú pháp và xóa các GOcâu lệnh sau BEGIN TRANSACTIONvà trước 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

Cũng lưu ý, tôi đã thêm một TRY...CATCHkhối câu lệnh để cho phép thực hiện một ROLLBACK TRANSACTIONcâu lệnh trong trường hợp xảy ra một số lỗi. Bạn có thể cần xử lý lỗi tốt hơn thế, nhưng không có kiến ​​thức về các yêu cầu của bạn, điều đó là khó khăn nhất.

Một số đọc tốt:

  1. Luôn chỉ định lược đồ

  2. Thủ tục lưu trữ Thực tiễn tốt nhất

  3. Những thói quen xấu cần tránh


1
Bạn vẫn muốn có một giao dịch được lưu. Nếu bạn đặt một giao dịch vào SP và SP bị gói trong một giao dịch khác sẽ thất bại. sqlstudies.com/2014/01/06/ trộm
Kenneth Fisher

cảm ơn đã chỉ cho tôi điều đó Tôi biết không có giao dịch lồng nhau trong SQL Server, tuy nhiên tôi không nhận thức được ý nghĩa của SAVE TRANSlệnh.
Max Vernon

8

Nếu bạn muốn xử lý đúng các Quy trình được lưu trữ lồng nhau có thể xử lý Giao dịch (cho dù bắt đầu từ T-SQL hoặc mã ứng dụng) thì bạn nên làm theo mẫu mà tôi đã mô tả trong câu trả lời sau:

Chúng tôi có bắt buộc phải xử lý Giao dịch bằng Mã C # cũng như trong thủ tục được lưu trữ không

Bạn sẽ nhận thấy hai sự khác biệt ở đó so với những gì bạn đang cố gắng ở đây:

  1. Việc sử dụng RAISERRORtrong CATCHkhối. Điều này tạo ra lỗi cho đến mức gọi (cho dù ở lớp DB hay ứng dụng) để có thể đưa ra quyết định liên quan đến thực tế là đã xảy ra lỗi.

  2. Không SAVE TRANSACTION. Tôi chưa bao giờ tìm thấy một trường hợp cho việc sử dụng này. Tôi biết một số người thích nó, nhưng trong tất cả mọi thứ tôi đã từng làm ở bất kỳ nơi nào tôi đã làm việc, khái niệm về lỗi xảy ra trong bất kỳ cấp độ lồng nhau nào đều ngụ ý rằng bất kỳ công việc nào đã được thực hiện đều không hợp lệ. Bằng cách sử dụng, SAVE TRANSACTIONbạn chỉ quay trở lại trạng thái ngay trước khi Thủ tục được lưu trữ này được gọi, để lại quy trình hiện tại như hợp lệ.

    Nếu bạn muốn biết thêm chi tiết SAVE TRANSACTION, xin vui lòng xem thông tin trong câu trả lời này:

    Cách khôi phục khi 3 thủ tục được lưu trữ được bắt đầu từ một thủ tục được lưu trữ

    Một vấn đề khác SAVE TRANSACTIONlà một sắc thái của hành vi của nó, như đã lưu ý trong trang MSDN cho SAVE TRANSACTION (nhấn mạnh thêm):

    Tên điểm lưu trữ trùng lặp được cho phép trong một giao dịch, nhưng câu lệnh ROLLBACK TRANSACTION chỉ định tên điểm lưu trữ sẽ chỉ đưa giao dịch trở lại Giao dịch TIẾT KIỆM gần đây nhất bằng cách sử dụng tên đó.

    Có nghĩa là, bạn cần hết sức cẩn thận để cung cấp cho mỗi Điểm lưu trong mỗi Quy trình được lưu trữ một tên duy nhất trên tất cả các Điểm lưu trong tất cả các Quy trình được lưu trữ. Các ví dụ sau minh họa điểm này.

    Ví dụ đầu tiên này cho thấy những gì xảy ra khi bạn sử dụng lại tên Save Point; chỉ Điểm lưu trữ cấp thấp nhất được khôi phục.

    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

    Ví dụ thứ hai này cho thấy những gì xảy ra khi bạn sử dụng tên Save Point duy nhất; Điểm lưu trữ của cấp độ mong muốn được khôi phục.

    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>

Đó là lý do tại sao tôi sử dụng @@ NESTLEVEL để chế tạo tên điểm lưu trữ TIẾT KIỆM GIAO DỊCH của mình.
Vincent Wrapsalbergh
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.