การทำธุรกรรมในขั้นตอนการจัดเก็บ


12

ฉันต้องการดำเนินการ UPDATE และ INSERT ในธุรกรรมเดียว รหัสนั้นทำงานได้ดีในตัวของมันเอง แต่ฉันต้องการที่จะสามารถเรียกมันได้อย่างง่ายดายและผ่านพารามิเตอร์ที่ต้องการ เมื่อฉันพยายามทำธุรกรรมนี้ในขั้นตอนการจัดเก็บฉันพบข้อผิดพลาดทางไวยากรณ์มากมาย

ฉันจะแค็ปซูลโค้ดต่อไปนี้เพื่อให้สามารถเรียกได้อย่างง่ายดาย?

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
อาจเป็นไปได้หากคุณเพิ่มรายละเอียดลงในคำถามของคุณเกี่ยวกับ "ข้อผิดพลาด" (ใช้ลิงค์แก้ไขใต้คำถามของคุณ) นอกจากนี้โปรดใช้การท่องเที่ยว ขอบคุณ!
Max Vernon

คำตอบ:


15

คุณชอบที่จำเป็นที่จะต้องตัดรหัสว่าในCREATE PROCEDURE ...ไวยากรณ์และลบGOงบหลังและก่อนที่BEGIN TRANSACTIONCOMMIT 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
คุณยังต้องการธุรกรรมที่บันทึกไว้ หากคุณทำธุรกรรมใน SP และ SP ได้รับการห่อในธุรกรรมอื่นสิ่งที่กำลังจะล้มเหลว sqlstudies.com/2014/01/06/…
Kenneth Fisher

ขอบคุณที่ชี้นำฉัน ฉันรู้ว่าไม่มีธุรกรรมซ้อนใน SQL Server แต่ฉันไม่ทราบถึงSAVE TRANSความหมายของคำสั่ง
Max Vernon

8

หากคุณต้องการจัดการขั้นตอนการจัดเก็บซ้อนที่สามารถจัดการธุรกรรม (ไม่ว่าจะเริ่มจาก T-SQL หรือรหัสแอป) อย่างถูกต้องคุณควรทำตามแม่แบบที่ฉันอธิบายไว้ในคำตอบต่อไปนี้:

เราจำเป็นต้องจัดการธุรกรรมในรหัส C # หรือในขั้นตอนการจัดเก็บ

คุณจะสังเกตเห็นความแตกต่างสองประการจากสิ่งที่คุณพยายามทำที่นี่:

  1. การใช้RAISERRORภายในCATCHบล็อก ข้อผิดพลาดนี้จะทำให้เกิดข้อผิดพลาดจนถึงระดับการโทร (ไม่ว่าจะเป็นในฐานข้อมูลหรือเลเยอร์แอป) ดังนั้นการตัดสินใจสามารถทำได้โดยคำนึงถึงข้อเท็จจริงที่ว่ามีข้อผิดพลาดเกิดขึ้น

  2. SAVE TRANSACTIONไม่ ฉันไม่เคยพบเคสสำหรับใช้สิ่งนี้ ฉันรู้ว่าบางคนชอบมัน แต่ในทุกสิ่งที่ฉันเคยทำในสถานที่ใด ๆ ที่ฉันเคยทำงานความคิดเกี่ยวกับข้อผิดพลาดที่เกิดขึ้นภายในระดับที่ซ้อนกันใด ๆ บ่งบอกว่างานใด ๆ ที่ทำไปแล้วนั้นไม่ถูกต้อง โดยการใช้งานSAVE TRANSACTIONคุณจะคืนค่ากลับสู่สถานะก่อนที่จะเรียกขั้นตอนการจัดเก็บนี้โดยปล่อยให้กระบวนการที่มีอยู่นั้นถูกต้อง

    หากคุณต้องการรายละเอียดเพิ่มเติมSAVE TRANSACTIONกรุณาลองดูข้อมูลในคำตอบนี้:

    วิธีการย้อนกลับเมื่อเริ่ม 3 โพรซีเดอร์ที่เก็บไว้จากโพรซีเดอร์ที่เก็บไว้หนึ่งโพรซีเดอร์

    ปัญหาอีกประการหนึ่งSAVE TRANSACTIONคือความแตกต่างของพฤติกรรมตามที่ระบุไว้ในหน้า MSDN สำหรับการบันทึกรายการ (เน้นการเพิ่ม):

    ชื่อจุดบันทึกซ้ำได้รับอนุญาตในการทำธุรกรรม แต่คำสั่ง ROLLBACK TRANSACTION ที่ระบุชื่อ savepoint จะย้อนกลับการทำธุรกรรมกลับไปที่การทำธุรกรรมSAVE ล่าสุดโดยใช้ชื่อนั้น

    หมายความว่าคุณต้องระมัดระวังเป็นอย่างยิ่งที่จะให้แต่ละจุดบันทึกในแต่ละขั้นตอนการจัดเก็บชื่อที่ไม่ซ้ำกันในทุกจุดบันทึกในขั้นตอนการจัดเก็บทั้งหมด ตัวอย่างต่อไปนี้แสดงให้เห็นถึงจุดนี้

    ตัวอย่างแรกนี้แสดงสิ่งที่เกิดขึ้นเมื่อคุณใช้ชื่อจุดบันทึกอีกครั้ง เฉพาะจุดบันทึกระดับต่ำสุดเท่านั้นที่ย้อนกลับ

    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

    ตัวอย่างที่สองนี้แสดงสิ่งที่เกิดขึ้นเมื่อคุณใช้ชื่อจุดบันทึกเฉพาะ จุดบันทึกของระดับที่ต้องการนั้นย้อนกลับ

    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 เพื่อสร้างชื่อจุดบันทึกการทำธุรกรรมการบันทึกของฉัน
Vincent Vancalbergh
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.