อย่าใช้ธุรกรรมสำหรับกระบวนงานที่เก็บไว้


18

ฉันมีขั้นตอนการจัดเก็บที่รันคำสั่งไม่กี่คำ ฉันไม่ต้องการให้ห่อคำสั่งเหล่านี้ในธุรกรรมของกระบวนงานที่เก็บไว้ หากคำสั่งที่ 4 ล้มเหลวฉันต้องการให้อันดับที่ 1, ที่ 2 และที่ 3 อยู่และไม่ย้อนกลับ

เป็นไปได้ไหมที่จะเขียนโพรซีเดอร์ที่เก็บไว้ในลักษณะที่มันไม่ได้ดำเนินการทั้งหมดในการทำธุรกรรมครั้งใหญ่?

คำตอบ:


16

ธุรกรรมทั้งหมดจะไม่ดำเนินการในการทำธุรกรรมเดียว ดูตัวอย่างนี้:

use TestDB;
go

if exists (select 1 from sys.tables where object_id = object_id('dbo.TestTranTable1'))
    drop table dbo.TestTranTable1;
create table dbo.TestTranTable1
(
    id int identity(1, 1) not null,
    some_int int not null
        default 1
);
go

insert into dbo.TestTranTable1
default values;
go 4

select *
from dbo.TestTranTable1;

if exists (select 1 from sys.sql_modules where object_id = object_id('dbo.ChangeValues'))
begin
    drop proc dbo.ChangeValues;
end
go
create proc dbo.ChangeValues
as
    update dbo.TestTranTable1
    set some_int = 11
    where id = 1;

    update dbo.TestTranTable1
    set some_int = 12
    where id = 2;

    update dbo.TestTranTable1
    set some_int = 13
    where id = 3;

    -- this will error out (arithmetic overflow)
    update dbo.TestTranTable1
    set some_int = 2147483648
    where id = 4;
go

exec dbo.ChangeValues;

select *
from dbo.TestTranTable1;

นี่คือผลลัพธ์:

ป้อนคำอธิบายรูปภาพที่นี่

โดยการสร้างเซสชันเพิ่มเติมเพื่อติดตามsql_transactionเหตุการณ์นี่คือผลลัพธ์จากการดำเนินการdbo.ChangeValues:

ป้อนคำอธิบายรูปภาพที่นี่

ดังที่คุณเห็นในภาพหน้าจอด้านบนนี้มีการทำธุรกรรมแยกต่างหากสำหรับแต่ละคำสั่งทั้งสี่ 3 รายการแรกกระทำและรายการสุดท้ายย้อนกลับเนื่องจากข้อผิดพลาด


16

ผมคิดว่าอาจจะมีความสับสนบางอย่างที่นี่เกี่ยวกับชุดเอสการทำธุรกรรม

การทำธุรกรรมเป็นคำสั่งหรือชุดของคำสั่งที่จะประสบความสำเร็จหรือล้มเหลวเป็นหน่วย คำสั่ง DDL ทั้งหมดอยู่ในการทำธุรกรรมด้วยตนเอง (เช่นถ้าคุณอัปเดต 100 แถว แต่แถว 98 มีข้อผิดพลาดจะไม่มีการอัปเดตแถวใด ๆ ) คุณสามารถตัดชุดของงบในการทำธุรกรรมเช่นเดียวกับที่ใช้BEGIN TRANSACTIONแล้วทั้งสองหรือCOMMITROLLBACK

ชุดคือชุดของงบที่จะดำเนินการร่วมกัน กระบวนงานที่เก็บไว้เป็นตัวอย่างของชุดงาน ในโพรซีเดอร์ที่เก็บไว้หากคำสั่งหนึ่งล้มเหลวและมีการดักข้อผิดพลาด (ปกติTRY/CATCHบล็อก) คำสั่งที่ตามมาจะไม่ดำเนินการ

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


ฉันไม่พบบทความใด ๆ ที่ระบุว่า "ขั้นตอนการจัดเก็บเป็นตัวอย่างของแบตช์" ฉันเชื่อว่า Stored Procedure นั้นคล้ายกับแบทช์มาก แต่ไม่ใช่แบทช์ ความแตกต่างที่สำคัญคือ: SP รับประกันว่าจะรวบรวมล่วงหน้าและพร้อมสำหรับการดำเนินการหลายครั้งซึ่งแตกต่างจากแบทช์ ความคล้ายคลึงกันคือ: - พวกเขาดำเนินการแต่ละคำสั่งพร้อมกัน - ถ้าหนึ่งคำสั่งล้มเหลวคำสั่งก่อนหน้านี้ทั้งหมดจะถูกคอมมิต (ยกเว้นว่ามันกำลังรันอยู่ในทรานแซคชัน) - หากคำสั่งหนึ่งล้มเหลวคำสั่งถัดไปทั้งหมดจะไม่ถูกดำเนินการ
Ashi

6

ทุกอย่างในเซิร์ฟเวอร์ sql มีอยู่ในการทำธุรกรรม

เมื่อคุณระบุอย่างชัดเจนbegin transactionและend transactionจากนั้นก็จะเรียกว่าการทำธุรกรรมที่ชัดเจน เมื่อคุณไม่ได้แล้วมันจะทำธุรกรรมโดยปริยาย

หากต้องการสลับโหมดที่คุณอยู่คุณจะต้องใช้

set implicit_transactions on

หรือ

set implicit_transactions off

select @@OPTIONS & 2

ถ้าข้างต้นกลับ 2 คุณอยู่ในโหมดการทำธุรกรรมโดยปริยาย ถ้ามันคืนค่า 0 แสดงว่าคุณกำลังอยู่ในระบบบันทึกอัตโนมัติ

การทำธุรกรรมคือทั้งหมดหรือไม่มีอะไรที่จะเก็บฐานข้อมูลในสถานะที่สอดคล้อง .. จำคุณสมบัติของกรด

CREATE TABLE [dbo].[Products](
    [ProductID] [int] NOT NULL,
    [ProductName] [varchar](25) NULL,
    [DatabaseName] [sysname] NOT NULL,
 CONSTRAINT [pk_Product_ID_ServerName] PRIMARY KEY CLUSTERED 
(
    [ProductID] ASC,
    [DatabaseName] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

GO


-- insert some data 
INSERT INTO [dbo].[Products]([ProductID], [ProductName], [DatabaseName])
SELECT 1, N'repl1_product1', N'repl1' UNION ALL
SELECT 1, N'repl2_product1_02', N'repl2' UNION ALL
SELECT 1, N'repl3_product1_03', N'repl3' UNION ALL
SELECT 2, N'repl1_product1_01', N'repl1' UNION ALL
SELECT 2, N'repl2_product1', N'repl2' UNION ALL
SELECT 2, N'repl3_product1_03', N'repl3' UNION ALL
SELECT 3, N'repl1_product1_01', N'repl1' UNION ALL
SELECT 3, N'repl2_product1_02', N'repl2' UNION ALL
SELECT 3, N'repl3_product1', N'repl3' UNION ALL
SELECT 4, N'repl1_product1_01', N'repl1' UNION ALL
SELECT 4, N'repl2_product1_02', N'repl2' UNION ALL
SELECT 5, N'repl1_product1_01', N'repl1' UNION ALL
SELECT 5, N'repl2_product1_02', N'repl2'

- สร้าง SP ทันที - โปรดทราบว่า 3 รายการแรกจะประสบความสำเร็จและอันดับที่ 4 จะล้มเหลวเนื่องจากการตัดสตริง ...

IF OBJECT_ID ('usp_UpdateProducts', 'P') IS NOT NULL
    DROP PROCEDURE usp_UpdateProducts;
GO
create procedure usp_UpdateProducts
as 
begin try
update Products 
set ProductName = 'repl1_product1'
where DatabaseName = 'repl1'and ProductID = 1;
update Products
set ProductName = 'repl2_product1'
where DatabaseName = 'repl2' and ProductID = 2;
update Products
set ProductName = 'repl3_product1'
where DatabaseName = 'repl3' and ProductID = 3;
update Products
set ProductName = 'repl3_product1_03&&&&&&&&&&39399338492w9924389234923482' -- this will fail ...
where DatabaseName = 'repl3' and ProductID = 4;
SELECT 1/0;
end try
begin catch
SELECT 
        ERROR_NUMBER() AS ErrorNumber,
        ERROR_SEVERITY() AS ErrorSeverity,
        ERROR_STATE() as ErrorState,
        ERROR_PROCEDURE() as ErrorProcedure,
        ERROR_LINE() as ErrorLine,
        ERROR_MESSAGE() as ErrorMessage;
end catch
go

อ้างอิงถึง: เป็นการปฏิบัติที่ไม่ถูกต้องหรือไม่ที่จะสร้างธุรกรรมอยู่เสมอ?


3

นี่คือวิธีการจัดเก็บตามค่าเริ่มต้น ขั้นตอนการจัดเก็บไม่ได้ห่อภายในการทำธุรกรรมโดยอัตโนมัติ

ถ้าคุณต้องการให้โพรซีเดอร์ที่เก็บไว้หยุดเมื่อพบข้อผิดพลาดแรกคุณจะต้องใส่ชื่อล็อกอิน TRY / CATCH บางส่วนในนั้นเพื่อส่งคืนในกรณีที่เกิดปัญหากับคำสั่ง 2


2

คุณจะต้องทำธุรกรรมแต่ละรายการสำหรับแต่ละคำสั่ง คุณสามารถทำสิ่งนี้ได้ด้วยธุรกรรมที่บันทึกไว้:

ดูSAVE TRANSACTION (Transact-SQL)ในเอกสารประกอบของผลิตภัณฑ์

ฉันต้องการรับรองว่าการทำธุรกรรมแต่ละรายการเป็นพฤติกรรมเริ่มต้นสำหรับขั้นตอนการจัดเก็บเนื่องจากข้อความทั้งหมดถูกห่อในธุรกรรมโดยนัย อย่างไรก็ตามไม่มีใครควรพึ่งพาธุรกรรมโดยนัยในการควบคุมโชคชะตาของรหัสของพวกเขา เป็นวิธีปฏิบัติที่ดีกว่ามากในการควบคุมวิธีจัดการธุรกรรมในรหัสการผลิตอย่างชัดเจน


-2

แยกแต่ละส่วนด้วย BEGIN TRAN และตรวจสอบว่าการทำธุรกรรมสำเร็จหรือไม่ ถ้ามันกระทำมันเป็นอย่างอื่นทำย้อนกลับเนื่องจากพวกเขาทั้งหมดกำลังดำเนินการจากระดับเดียวกันคุณจะสามารถกระทำแต่ละส่วนแยกกันโดยไม่ต้องย้อนกลับทั้งหมดหากล้มเหลว

สำหรับข้อมูลเพิ่มเติมที่คุณสามารถดูที่: http://msdn.microsoft.com/en-us/library/ms188929.aspx


1
จะสร้างธุรกรรมย่อยภายในกระบวนงานที่เก็บไว้ของฉันหรือไม่ เป็นการดีที่ฉันต้องการหลีกเลี่ยงสิ่งนั้นถ้าเป็นไปได้
Matthew Steeples

1
หาก SP ถูกเรียกจากภายในธุรกรรมธุรกรรมที่บันทึกไว้ข้างต้นคือคำตอบ หากไม่ได้เรียก sp ด้วย @mrdenny นั้นถูกต้อง เซิร์ฟเวอร์ Sql ไม่รองรับธุรกรรมที่ซ้อนกัน
StrayCatDBA

@StrayCatDBA เพียงชี้แจง .. มีธุรกรรมที่ซ้อนกันใน SQL Server แต่พวกเขาชั่วร้าย .. SQL Server ช่วยให้คุณสามารถเริ่มทำธุรกรรมภายในธุรกรรมอื่น ๆ - เรียกว่าธุรกรรมซ้อน อ้างถึงsqlskills.com/blogs/paul/… , msdn.microsoft.com/en-us/library/ms189336(v=sql.105).aspxและsqlblog.com/blogs/kalen_delaney/archive/2007/08/13 / …
Kin Shah

2
เพื่อความชัดเจน (และสำหรับคนขี้เกียจที่ไม่ต้องการคลิกลิงก์) คุณไม่ได้เริ่มทำธุรกรรมอื่น ตั้งชื่อในโพสต์ของ Paul: "Myth: ธุรกรรมซ้อนเป็นของจริง" พวกเขาไม่ใช่ธุรกรรมจริง COMMIT ในการทำธุรกรรมที่ซ้อนกันไม่ทำอะไรเลยยกเว้นการลดลง @@ TRANCOUNT เป็นความจริงที่ว่าคุณจะไม่ได้รับข้อผิดพลาดหากคุณซ้อน BEGIN TRAN / COMMIT ไว้ แต่นั่นแตกต่างจากการทำธุรกรรมซ้อนที่แท้จริง
StrayCatDBA
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.