Jangan gunakan transaksi untuk Prosedur Tersimpan

18

Saya memiliki prosedur tersimpan yang menjalankan beberapa perintah. Saya tidak ingin perintah ini dibungkus dengan transaksi dari prosedur tersimpan. Jika perintah ke-4 gagal, saya ingin yang ke-1, ke-2 dan ke-3 tetap dan tidak kembalikan.

Apakah mungkin untuk menulis prosedur tersimpan sedemikian rupa sehingga tidak semua dijalankan sebagai satu transaksi besar?

Matthew Steeples
sumber

Jawaban:

16

Semua transaksi tidak akan dieksekusi dalam satu transaksi. Lihatlah contoh ini:

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;

Berikut hasilnya:

masukkan deskripsi gambar di sini

Dengan membuat sesi Extended Events untuk memantau sql_transactionacara, berikut adalah output dari pelaksanaan dbo.ChangeValues:

masukkan deskripsi gambar di sini

Seperti yang dapat Anda lihat pada tangkapan layar di atas, ada transaksi terpisah untuk masing-masing dari empat pernyataan. 3 komit pertama, dan yang terakhir memutar kembali karena kesalahan.

Thomas Stringer
sumber
16

Saya pikir mungkin ada beberapa kebingungan di sini tentang batch vs transaksi .

Sebuah transaksi adalah pernyataan atau set pernyataan yang baik akan berhasil atau gagal sebagai satu unit. Semua pernyataan DDL dalam transaksi itu sendiri (yaitu jika Anda memperbarui 100 baris tetapi baris 98 menimbulkan kesalahan, tidak ada baris yang diperbarui). Anda dapat membungkus serangkaian pernyataan dalam suatu transaksi juga menggunakan BEGIN TRANSACTIONdan kemudian salah satu COMMITatau ROLLBACK.

Sebuah batch yang merupakan serangkaian pernyataan yang dijalankan bersama-sama. Prosedur tersimpan adalah contoh batch. Dalam prosedur tersimpan, jika satu pernyataan gagal dan ada perangkap kesalahan (biasanya TRY/CATCHblok) maka pernyataan berikutnya tidak akan dijalankan.

Saya menduga masalah Anda adalah batch dibatalkan ketika terjadi kesalahan karena proc yang disimpan itu sendiri atau lingkup luar (seperti aplikasi atau proc yang disimpan yang menyebut prosedur ini) memiliki kesalahan dalam menjebaknya. Jika itu masalahnya, ini lebih sulit untuk diselesaikan karena Anda perlu menyesuaikan cara Anda menangani kesalahan pada ruang lingkup apa pun yang menjebaknya.

JNK
sumber
Saya tidak menemukan artikel yang mengatakan, "Prosedur penyimpanan adalah contoh kumpulan". Saya percaya Stored Procedure sangat mirip dengan batch tetapi ini bukan batch. Perbedaan utama adalah: SP dijamin akan dikompilasi terlebih dahulu dan siap untuk dieksekusi beberapa kali tidak seperti Batch. Kesamaannya adalah: - Mereka berdua menjalankan setiap perintah pada suatu waktu. - Jika satu perintah gagal maka semua perintah sebelumnya dilakukan (kecuali jika sedang berjalan dalam transaksi) - jika satu perintah gagal maka semua perintah selanjutnya tidak dijalankan.
Ashi
6

Segala sesuatu di server sql terkandung dalam transaksi.

Ketika Anda secara eksplisit menentukan begin transactiondan end transactionkemudian disebut Transaksi Eksplisit . Ketika Anda tidak, maka itu adalah transaksi implisit .

Untuk mengganti mode yang Anda gunakan, gunakan

set implicit_transactions on

atau

set implicit_transactions off

select @@OPTIONS & 2

jika di atas mengembalikan 2, Anda berada dalam mode transaksi implisit. Jika mengembalikan 0, Anda berada dalam autocommit.

Transaksi adalah SEMUA atau tidak sama sekali untuk menjaga basis data dalam keadaan konsisten .. ingat properti ACID.

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'

- buat SP sekarang - perhatikan bahwa 3 pertama akan berhasil dan 4 akan gagal karena pemotongan string ...

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

Lihat: Apakah praktik yang buruk untuk selalu membuat transaksi?

Kin Shah
sumber
3

Ini adalah cara kerja prosedur tersimpan secara default. Prosedur tersimpan tidak dibungkus dalam suatu transaksi secara otomatis.

Jika Anda ingin prosedur tersimpan berhenti ketika hits kesalahan pertama Anda ingin menaruh beberapa TRY / CATCH login di sana untuk kembali jika terjadi masalah dengan perintah 2 misalnya.

mrdenny
sumber
2

Anda akan membutuhkan transaksi individual untuk setiap perintah. Anda juga dapat melakukan ini dengan transaksi tersimpan:

Lihat SAVE TRANSACTION (Transact-SQL)di dokumentasi produk.

Saya ingin memastikan bahwa setiap transaksi merupakan perilaku default untuk prosedur tersimpan, karena semua pernyataan terbungkus dalam transaksi implisit; Namun, tidak seorang pun harus mengandalkan transaksi implisit untuk mengendalikan nasib kode mereka. Ini adalah praktik yang jauh lebih baik untuk secara eksplisit mengontrol cara transaksi ditangani dalam kode produksi.

Adam Haines
sumber
-2

pisahkan setiap bagian dengan BEGIN TRAN dan untuk memeriksa apakah transaksi berhasil. jika itu melakukan itu, kalau tidak lakukan rollback, karena mereka semua mengeksekusi dari tingkat yang sama Anda akan dapat melakukan setiap bagian secara terpisah tanpa harus mengembalikan semua jika salah satu gagal.

Untuk info lebih lanjut Anda dapat melihat di: http://msdn.microsoft.com/en-us/library/ms188929.aspx

Toni Kostelac
sumber
1
Apakah itu akan membuat sub transaksi dalam Prosedur Tersimpan saya? Idealnya saya ingin menghindari itu jika memungkinkan
Matthew Steeples
1
Jika SP dipanggil dari dalam suatu transaksi, maka transaksi yang disimpan di atas adalah jawabannya. Jika sp tidak dipanggil dengan, maka @rdenny sudah benar. Sql server tidak mendukung transaksi bersarang.
StrayCatDBA
@ StrayCatDBA hanya untuk memperjelas .. ada Transaksi Bertumpuk di SQL Server, tetapi mereka jahat .. SQL Server memungkinkan Anda untuk memulai transaksi di dalam transaksi lain - disebut transaksi bertingkat. Lihatlah sqlskills.com/blogs/paul/… , msdn.microsoft.com/en-us/library/ms189336(v=sql.105).aspx dan sqlblog.com/blogs/kalen_delaney/archive/2007/08/13 / ...
Kin Shah
2
Agar lebih jelas (dan bagi pemalas yang tidak ingin mengklik tautan), Anda sebenarnya tidak memulai transaksi lain. Aka judul pada posting Paul: "Mitos: Transaksi Bertumpuk adalah nyata." Itu bukan transaksi nyata. KOMIT dalam transaksi bersarang tidak melakukan apa pun kecuali penurunan @@ TRANCOUNT. Memang benar bahwa Anda tidak mendapatkan kesalahan jika Anda bersarang BEGIN TRAN / COMMIT, tetapi itu berbeda dari memiliki transksi bersarang nyata.
StrayCatDBA