ストアドプロシージャにトランザクションを使用しないでください


18

いくつかのコマンドを実行するストアドプロシージャがあります。これらのコマンドがストアドプロシージャのトランザクションにラップされないようにします。4番目のコマンドが失敗した場合、1番目、2番目、および3番目のコマンドをロールバックではなく、そのままにしておきます。

すべてが1つの大きなトランザクションとして実行されないような方法でストアドプロシージャを記述することは可能ですか?

回答:


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次のとおりです。

ここに画像の説明を入力してください

上記のスクリーンショットでわかるように、4つのステートメントのそれぞれに個別のトランザクションがあります。最初の3つはコミットし、最後の1つはエラーのためロールバックします。


16

ここではバッチトランザクションに関して何らかの混乱があると思います。

トランザクションは、いずれかの単位として成功または失敗するステートメントのステートメントまたはセットです。すべてのDDLステートメントはトランザクション自体にあります(つまり、100行を更新しても行98がエラーをスローした場合、行は更新されません)。トランザクション内で一連のステートメントをラップすることもできBEGIN TRANSACTIONます。次に、COMMITまたはを使用しますROLLBACK

バッチが一緒に実行される一連のステートメントです。ストアドプロシージャはバッチの例です。ストアドプロシージャでは、1つのステートメントが失敗し、エラートラップ(通常はTRY/CATCHブロック)がある場合、後続のステートメントは実行されません。

ストアドプロシージャ自体または外部スコープ(アプリケーションまたはこのプロシージャを呼び出すストアドプロシージャなど)にエラーがトラップされているため、エラーが発生したときにバッチがキャンセルされていると思われます。その場合、エラーをトラップしているスコープでエラーを処理する方法を調整する必要があるため、これを解決するのは難しくなります。


「ストアプロシージャはバッチの例です」という記事は見つかりませんでした。ストアドプロシージャはバッチに非常に似ていると思いますが、バッチではありません。主な違いは次のとおりです。SPは、バッチとは異なり、事前にコンパイルされ、複数回実行する準備ができていることが保証されています。類似点は次のとおりです。-両方とも一度に各コマンドを実行します。-1つのコマンドが失敗した場合、以前のすべてのコマンドがコミットされます(トランザクションで実行されていなかった場合)-1つのコマンドが失敗した場合、次のすべてのコマンドは実行されません。
アシ

6

SQLサーバーのすべてがトランザクションに含まれています。

明示的に指定begin transactionend transaction明示的トランザクションと呼ばれる場合。いけないときは、暗黙のトランザクションです。

現在のモードを切り替えるには、次を使用します

set implicit_transactions on

または

set implicit_transactions off

select @@OPTIONS & 2

上記が2を返す場合、暗黙のトランザクションモードになっています。0が返された場合、自動コミットされています。

トランザクションは、データベースを一貫した状態に維持するために、ALLまたはゼロです。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'

-今すぐ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

これは、デフォルトでストアドプロシージャが機能する方法です。ストアドプロシージャは、トランザクション内で自動的にラップされません。

最初のエラーが発生したときにストアドプロシージャを停止するには、たとえばコマンド2で問題が発生した場合に戻るために、TRY / CATCHログインを配置します。


2

コマンドごとに個別のトランザクションが必要になります。保存されたトランザクションでもこれを実現できます。

SAVE TRANSACTION (Transact-SQL)製品ドキュメントを参照してください。

すべてのステートメントは暗黙的なトランザクションにラップされているため、個々のトランザクションをストアドプロシージャのデフォルトの動作に限定します。ただし、コードの運命を制御するために暗黙のトランザクションに依存することはできません。本番コードでのトランザクションの処理方法を明示的に制御することをお勧めします。


-2

各部分をBEGIN TRANで区切り、トランザクションが成功したかどうかを確認します。それがコミットされた場合、そうでない場合はロールバックを行います。すべて同じレベルから実行されているため、1つのセクションが失敗してもロールバックすることなく各セクションを個別にコミットできます。

詳細については、http//msdn.microsoft.com/en-us/library/ms188929.aspxをご覧ください。


1
それは私のストアドプロシージャ内にサブトランザクションを作成しますか?理想的には、できればそれを避けたいと思います
マシュースティープルズ

1
SPがトランザクション内から呼び出される場合、上記の保存されたトランザクションが答えです。spが呼び出されない場合、@ mrdennyは正しいです。SQLサーバーはネストされたトランザクションをサポートしていません。
StrayCatDBA

@StrayCatDBAは、SQL Serverに入れ子になったトランザクションがありますが、邪悪です。SQLServerでは、入れ子になったトランザクションと呼ばれる他のトランザクション内でトランザクションを開始できます。参照してくださいsqlskills.com/blogs/paul/...msdn.microsoft.com/en-us/library/ms189336(v=sql.105).aspxsqlblog.com/blogs/kalen_delaney/archive/2007/08/13 / ...
キン・シャー

2
明確にするために(そして、リンクをクリックしたくない怠け者のために)、実際には別のトランザクションを開始していません。ポールの投稿のタイトル:「神話:入れ子になったトランザクションは本物です。」それらは実際のトランザクションではありません。ネストされたトランザクションでのCOMMITは、@@ TRANCOUNTの減分以外は何もしません。BEGIN TRAN / COMMITをネストしてもエラーが発生しないのは事実ですが、それは実際のネストされたトランザクションを持つこととは異なります。
StrayCatDBA
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.