Transaksi dalam prosedur tersimpan

12

Saya perlu melakukan UPDATE dan INSERT dalam satu transaksi. Kode itu berfungsi dengan baik sendiri, tetapi saya ingin dapat memanggilnya dengan mudah dan mengirimkan parameter yang diperlukan. Ketika saya mencoba untuk menumpuk transaksi ini dalam prosedur tersimpan saya mengalami banyak kesalahan sintaksis.

Bagaimana saya bisa merangkum kode berikut sehingga dapat dengan mudah dipanggil?

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
Charlie K.
sumber
1
Kemungkinan akan membantu jika Anda menambahkan detail ke dalam pertanyaan Anda tentang apa sebenarnya "kesalahan" itu (gunakan tautan edit di bawah pertanyaan Anda). Juga, silakan ikuti tur . Terima kasih!
Max Vernon

Jawaban:

15

Anda perlu membungkus kode itu dalam CREATE PROCEDURE ...sintaks, dan menghapus GOpernyataan setelah BEGIN TRANSACTIONdan sebelumnya 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

Juga perhatikan, saya telah menambahkan TRY...CATCHblok pernyataan untuk memungkinkan melakukan ROLLBACK TRANSACTIONpernyataan jika terjadi kesalahan. Anda mungkin memerlukan penanganan kesalahan yang lebih baik dari itu, tetapi tanpa sepengetahuan kebutuhan Anda, itu paling sulit.

Beberapa bacaan yang bagus:

  1. Selalu tentukan skema

  2. Prosedur Tersimpan Praktik Terbaik

  3. Kebiasaan buruk yang harus dihindari

Max Vernon
sumber
1
Anda masih ingin memiliki transaksi tersimpan. Jika Anda melakukan transaksi dalam SP dan SP dibungkus dengan hal-hal transaksi lain akan gagal. sqlstudies.com/2014/01/06/...
Kenneth Fisher
terima kasih telah mengarahkan saya pada hal itu. Saya tahu tidak ada transaksi bersarang di SQL Server, namun saya tidak menyadari SAVE TRANSimplikasi perintah itu.
Max Vernon
8

Jika Anda ingin menangani dengan benar Prosedur tersimpan yang tersimpan yang dapat menangani Transaksi (apakah dimulai dari T-SQL atau kode aplikasi) maka Anda harus mengikuti templat yang saya jelaskan dalam jawaban berikut:

Apakah kita diharuskan untuk menangani Transaksi dalam Kode C # dan juga dalam prosedur tersimpan

Anda akan melihat dua perbedaan di sana dari apa yang Anda coba di sini:

  1. Penggunaan RAISERRORdalam CATCHblok. Ini gelembung kesalahan hingga tingkat panggilan (baik di DB atau lapisan aplikasi) sehingga keputusan dapat dibuat mengenai fakta bahwa kesalahan terjadi.

  2. Tidak ada SAVE TRANSACTION. Saya belum pernah menemukan kasus untuk menggunakan ini. Saya tahu beberapa orang lebih menyukainya, tetapi dalam segala hal yang pernah saya lakukan di tempat saya pernah bekerja, gagasan tentang kesalahan yang terjadi di salah satu level bersarang menyiratkan bahwa pekerjaan apa pun yang sudah dilakukan tidak valid. Dengan menggunakan SAVE TRANSACTIONAnda hanya mengembalikan kembali ke negara sebelum Prosedur Disimpan ini dipanggil, meninggalkan proses yang ada sebagai dinyatakan sah.

    Jika Anda ingin detail lebih lanjut SAVE TRANSACTION, silakan lihat informasi dalam jawaban ini:

    Cara mengembalikan ketika 3 prosedur tersimpan dimulai dari satu prosedur tersimpan

    Masalah lainnya SAVE TRANSACTIONadalah nuansa perilakunya, sebagaimana dicatat dalam halaman MSDN untuk SIMPAN TRANSAKSI (penekanan ditambahkan):

    Nama savepoint duplikat diizinkan dalam transaksi, tetapi pernyataan TRANSAKSI ROLLBACK yang menentukan nama savepoint hanya akan mengembalikan transaksi ke SAVE TRANSACTION terbaru menggunakan nama itu.

    Artinya, Anda harus sangat berhati-hati untuk memberikan setiap Titik Simpan di setiap Prosedur yang Disimpan nama yang unik di semua Poin Simpan di semua Prosedur yang Disimpan. Contoh-contoh berikut menggambarkan hal ini.

    Contoh pertama ini menunjukkan apa yang terjadi ketika Anda menggunakan kembali nama Simpan Poin; hanya Save Point level terendah yang diputar kembali.

    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
    

    Contoh kedua ini menunjukkan apa yang terjadi ketika Anda menggunakan nama Simpan Poin yang unik; Save Point level yang diinginkan dibatalkan.

    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>
    
Solomon Rutzky
sumber
Itu sebabnya saya menggunakan @@ NESTLEVEL untuk mengarang nama savepoint SAVE TRANSACTION saya.
Vincent Vancalbergh