- - PR -
Transact-SQLでのトランザクション管理について
| 投稿者 | 投稿内容 | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
投稿日時: 2005-12-08 14:49
かなり基本的な質問ですが、Transact-SQLでトランザクション管理を行う方法 について教えてください。 ストアドプロシージャでテーブルの全件更新処理を行うため、下記のような処理を 記述したのですが、エラー時(下記ではエラーフラグが立った場合)ロールバックが 正しく行われませんでした(@まで処理が戻らず、全てコミットされた状態で 原因についてご存知でしたらご指摘頂けますでしょうか。 BEGIN -- 変数宣言 DECLARE ・・・ -- トランザクション開始 BEGIN TRANSACTION -- 全件更新対象テーブルを全件DELETE @ DELETE FROM 更新先テーブル -- 更新元データをセレクトしてカーソル定義 DECLARE cur01 CURSOR FOR SELECT ・・・・ FROM 更新元テーブル -- カーソルをオープンして更新元からレコードをFETCHする OPEN cur FETCH NEXT FROM cur01 INTO 変数 WHILE @@FETCH_STATUS = 0 BEGIN -- 更新元テーブルのデータをカラム単位でチェックする(参照処理のみ) -- もしデータに不整合があれば、エラーログを出力し、エラーフラグを -- 立てる。 IF チェックエラー SET ERRFLG = 1 エラー出力(テーブル変数へ書き出し) -- チェックが済んだデータを更新先テーブルへ登録する INSERT INTO 更新先テーブル VALUES ( カーソル上のデータ) -- 登録処理でエラーが発生した場合はトランザクションをロールバック する。 IF @@ERROR <> 0 ROLLBACK TRANSACTION -- 次のレコードを読み出す NEXT NEXT FROM cur01 INTO 変数 END カーソルループ処理で一件でもエラーがあれば、トランザクションを ロールバックする IF ERRFLG = 1 ROLLBACK TRANSACTION ELSE COMMIT TRANSACTION END | ||||||||||||||||||||||||
|
投稿日時: 2005-12-08 17:46
らいと申します。
記述ミスの可能性もありますが、いくつか。 まず、全体的に 変数には『@』をつけましょう。 で、本編。
SET @cur01 = Cursor For SELECT ・・・・ FROM 更新元テーブル かと思われます。
2行以上にわたるIF文は、 『Begin〜End』 でくくりましょう。
おそらく記述ミスだとは思いますが、 FETCH NEXT 〜 です。 多分、こんなところです。 あとは見えていないところで、変数の型には注意です。 _________________ 一寸先は闇 安定してるシステムって言ったじゃん(泣) | ||||||||||||||||||||||||
|
投稿日時: 2005-12-09 11:29
ループとか書いてあるのでおそらくループ内でERRFLGを正常処理のSQLの@@ERRORで上書いているんでしょうな。
たぶんプログラムミスですね。 | ||||||||||||||||||||||||
|
投稿日時: 2005-12-09 12:35
らいさん
迅速な回答ありがとうございました。以下コメントします。
失礼しました。書き込み用に記載したものなので、 文法を無視してしまいました。混乱させてしまいました。
カーソルについては、SQLServerのBooksOnlineの記述を利用したのですが上記 の記述の方が正しいのでしょうか。
失礼しました。実際のコードは括っています。
失礼しました。こちらも転記の際のミスです。
有難うございました。 記載したコードでは、カーソルをループさせる箇所では全く COMMITせず、ループを抜けた後にコミットするようにしております。 クエリアナライザでデバックしたのですが、ループ中は COMMITのステップへは移っておりません。 しかし実行結果をクエリアナライザのメッセージタブで参照すると、 ループ回数分(1 件処理されました)というメッセージが出ています。 意図的に途中でロールバックするようなデータを試してみたのですが、 ロールバックはされていませんでした。 SQLServerでの記述は初めてなのですが、暗黙的にCOMMITされてしまう ということはありませんでしょうか。 | ||||||||||||||||||||||||
|
投稿日時: 2005-12-09 12:37
らいです。
ここですが、 SELECT @ERRFLG = 1 でもいいかと思うのですが、なぜSETなのでしょうか。 つーか、昨日の時点で気づけよ、自分。orz
どうでしょうかねぇ。 ただ、ループでも変数は初期化されないだろうし、 ERRFLGを書き換えているのは エラーチェックでエラーと判断された場合(?)のみみたいだし。 # 見えてないところでやっているんだったらお手上げですが。(笑) これから後は、質問者さんがもう一回出てきてからでしょうね。 _________________ 一寸先は闇 安定してるシステムって言ったじゃん(泣) | ||||||||||||||||||||||||
|
投稿日時: 2005-12-09 12:44
Anthyhimeさん
ご回答ありがとうございます。 コードを全て転記しておらず、記載ミスもあったので、分かりにく かったかと思います。すみませんでした。
ERRFLGの値を@@ERRORで上書きするような記述はありませんでした。 私もプログラムミスだと思うのですが、上述の通りカーソルループ 内にはコミットするような記述を記載していないにもかかわらず、 ループ毎にコミットするような挙動が見受けられます。 同じようなご経験はありませんでしょうか。 | ||||||||||||||||||||||||
|
投稿日時: 2005-12-09 13:20
らいです。
度々すいません。 えと、もしかしたら、トランザクションの制御かもしれません。 もし、エラーが起こってしまった場合は
の下でロールバックしています。 そうすると、トランザクションが終わって 自動コミットになってしまうのでは? ということで、 FETCHでカーソルをグリグリやってるところの一番初めで BEGIN TRANSACTION を宣言してみてはいかがでしょうか。 もちろん、ELSE文を作成して、 COMMIT TRANSACTION をする必要もありますが。 だめかなぁ。 でも、ここでエラーが出てないってのは別にいいのかなぁ。 # トランザクションの数が合わないと、怒られるような記憶が。 あと、
ですが、どうなんだろうなぁ。 私の場合、BooksOnlineを読んだことないので。(^^;; _________________ 一寸先は闇 安定してるシステムって言ったじゃん(泣) | ||||||||||||||||||||||||
|
投稿日時: 2005-12-09 13:39
ストアドプロシージャーはどのように呼び出されているでしょうか?もし ADO などのミドルウェアを使ってプログラム中から呼ばれているとなにか副作用があるのかもしれません。isql や osql などで呼ばれても同じ現象でしょうか。
なお、SQL Server は Oracle(?) などと違って、暗黙的にコミットするということは基本的になく、わりと素直です。BEGIN TRANSACTION したのならば COMMIT TRANSACTION しない限り、コミットされることは絶対に(?)なかったと思います。 試しに、ストアドプロシージャー中の COMMIT TRANSACTION を完全に取り除いてみて、それでもコミットされるかどうかを調べられてはどうでしょうか。 | ||||||||||||||||||||||||
