• ベストアンサー

一つのトランザクションでSELECTとUPDATEできますか? (ADO.NET)

いつもお世話になっております。 ADO.NETの話なのですが、トランザクションを開始したコネクションオブジェクトで DataAdapterを使ったSELECTと、ExecuteNonQueryを使ったUPDATEを交互に繰り返し 行うことはできるでしょうか? 現状できていないので可能なのであれば共通関数の見直しが必要かと思っています。 いまはSELECTとUPDATEを別のコネクションで行っているのですが、UPDATE後に 同じテーブルの該当レコードをSELECTにいくため、デッドロックが発生しています。

質問者が選んだベストアンサー

  • ベストアンサー
  • 7marine
  • ベストアンサー率36% (59/160)
回答No.3

>それにしてもコネクションとトランザクションオブジェクトを別々に >渡してやる必要性がよく理解できません。コネクションオブジェクトから >トランザクションオブジェクトも参照できるのでは?と思うのですが・・・。 たしかに私も腑に落ちない・・・気がします なにか別に設定が必要な理由があるのかも知れませんね 複数のトランザクションが存在する場合に指定が必要になるのかも! 私の過去のコードにはしっかり sqlCommand.Transaction = tran がありました(いつの間にか記述してたみたい) すいません。#2ではまったくその事に触れていませんでした 憶測での回答になってしまいますが トランザクションが割り当てられたコマンドが実行されたときに 自動的にBegin Tranされ トランザクションが割り当てられていないコマンドが実行されたときに 自動的にCommit Tranが実行されてしまうのかも知れません 時間があったら実証して見たいと思います

その他の回答 (2)

  • 7marine
  • ベストアンサー率36% (59/160)
回答No.2

実はあまり知られていないことなのですが DataAdapterは複数のテーブルまたは全く関連のないテーブル、さらに同時に複数のテーブル、さらにさらにストアドプロシージャを指定してselect,update,insert,deleteを行うことが可能です その方法は割愛しますが、ウィザードだけを使用していると見落としがちですね(笑 ただコーディング実装が難しい部類になるので上級者むけですが 1~4を全てDataAdapterだけで実現することも可能です。 更新の反映はDataAdapter.Update()だけで済むのですが CommandオブジェクトとTableMappingの関連付けがやや難しいです 自信が無ければ上記は一度忘れて構いません 別の解決方法 1、DataAdapterのSelectCommandのConnectionをUpdateコマンドのConnectionに割り当てること 2、FillやUpdateを行う前にConnectionをOpenし全ての処理が終わってからCloseすること 何故そうするか? DataAdapterのFillコマンドはSelectCommandのConnectionの状態をFill前と後で維持します。 冗長して説明するとFillする前にCloseである場合は自動的にOpenしてからFillの後に自動的にCloseします。逆にOpenである場合はFillのみ処理を行います。 ですのでFillを行う前にConnectionをOpenする処理が無ければFill後に一度Closeしようとするのでトランザクションがぶつ切りになる可能性があります。updateまで含めて一連のトランザクションしたいのであれば、わざわざ手動でConnectionをOpen・Closeする必要があります。 >DataAdapterをCloseしたら、ExecuteNonQueryも >使えるようになるんですかね? 先の質問の答えはYesですが、一度CloseしてCommandオブジェクトのExecuteNonQueryを実行した場合はトランザクションの意味がなくなる可能性があります

kamuatatsu
質問者

お礼

アドバイスありがとうございます。 共通関数を使わずにDataAdapterとExecuteNonQueryを使ってみたところ、 トランザクションを開始した接続オブジェクトで問題なく動作しました。 共通関数の中身を確認したところ、データアダプター使用時に Da = New SqlDataAdapter(strSQL, SqlCn) と宣言していたのが問題だったようです。 トランザクションを開始した接続オブジェクトでデータアダプターを 使うには、下記のような形でトランザクションオブジェクトも指定して やることが必要でした。 Da = New SqlDataAdapter Da.SelectCommand = New SqlCommand(strSQL, SqlCn, Trans) それにしてもコネクションとトランザクションオブジェクトを別々に 渡してやる必要性がよく理解できません。コネクションオブジェクトから トランザクションオブジェクトも参照できるのでは?と思うのですが・・・。

  • 7marine
  • ベストアンサー率36% (59/160)
回答No.1

可能です。ただ疑問があります。 なぜDataAdapterでUpdateの処理まで行わないのでしょう。 推測ですが。。。 DataTableに結合クエリでfillしているのであれば UpdateCommand,InsertCommand,DeleteCommandを 自作する必要がありますが、同じようなことはできるかもしれません

kamuatatsu
質問者

補足

結合クエリというのはちょっと分からないのですが、 DataAdapterでUPDATEしない理由は、単純に1つの テーブルをSELECTしてUPDATEするわけ ではないことと、もう一つはSELECTしてくる共通関数 がUPDATEを考慮していないことが原因です。 (1)テーブルA から SELECT (2)(1)のデータでテーブルB から SELECT (3)(2)のデータでテーブルC に INSERT (4)テーブルA をUPDATE 上記の処理を繰り返し行うようなロジックになっています。 DataAdapterをCloseしたら、ExecuteNonQueryも 使えるようになるんですかね?

関連するQ&A

  • トランザクション中にSELECTした場合について

    InnoDBを使っているのですが、 以下の1.から5.の処理の間に、サーバー1.の「テーブルA」へ SELECTを掛けた場合、何か遅い気がします・・原因は何でしょうか? 1.サーバー1でトランザクション開始 2.サーバー1の「テーブルA」をDELETEでを全削除 3.サーバー2から「テーブルA」の新しいデータを取得 4.3.で取得したデータをサーバー1の「テーブルA」へINSERT 5.サーバー1でトランザクション終了 他のトランザクションでDELETEされたレコードに対し、 SELECTすると結果が返るのは遅いでしょうか?

    • ベストアンサー
    • MySQL
  • select for updateのロック

    オラクルのselect for updateでロックをするタイミングがいつですか? こんなPL/SQLのコードがあったとします。 ---↓↓↓ソースコードここから↓↓↓------------------------- select * from テーブル1 where id = 1 for update; ・・・・・(a) ~ update テーブル1 set kingaku=100 where id = 1 ・・・・・(b) ~ commit; ---↑↑↑ソースコードここまで↑↑↑------------------------- id = 1のレコードがロックされるのは(a)、(b)どちらのタイミングですか? また、このロックは ・他トランザクションから読めるけど更新できない ・他トランザクションからは読むことすらできない のどちらでしょうか? よろしくお願いします。

  • SELECT文でのデッドロックに対しての対処方

    先日よりデッドロックが発生するようになり、確認の為にトレースログを 抽出するように設定かけたのですが、UPDATE文とSELECT文がぶつかり デッドロックが発生している事がわかりました。 しかし、UPDATE文にはトランザクションをかけていますが SELECT文には特にロックかけていない為、どうして起こっているのか わかっていません。 SELECT文を読んでいる時にUPDATE文によって レコードに変更があった為、デッドロックが発生したのでしょうか? SELECT文にはUNLOCKをつけた方がいいのでしょうか? プログラムはDELPHI DBはSQLSERVER2000になります。 お忙しい所申し訳ありません。 宜しくお願い致します。

  • Sqlトランザクションの必要性

    SQLServer2005Expressを使用しています。 そこでトランザクションについて質問させて頂きます。 複数のスレッドや複数の端末から1つのテーブルの1つのレコードを 更新する場合に、トランザクションを掛ける必要性はあるでしょうか。 複数のセットとなるテーブルを更新する場合はトランザクションで 整合性を保ちつつ排他を行うので必須と思いますが、 1つのテーブルの1つのレコードの場合も必須でしょうか。 また、必須ではなくてもトランザクションを掛けておく有効な理由は あるでしょうか。 ちなみにトランザクション中は、他のコネクションから参照も不可に なるようにしております。 よろしく願いいたします。

  • SELECT FOR UPDATE について

    ■SELECT FOR UPDATEとは、 複数のプログラムで在庫の更新などが行われる場合に、 使用するべきでしょうか? ■SELECT FOR UPDATEのロック期間は、他の更新が待たされるのは 下記2.~3.の間でしょうか? バッチ更新(5分) 1.トランザクション開始 2.SELECT FOR UPDATE 3.UPDATE 4.トランザクション終了 ■上記2.のSELECT FOR UPDATEがおこなれた時点のレコードに対し、  他の場所でSELECT FOR UPDATEした場合、  上記2.の時点のデータがSELECTされるのでしょうか?  それとも、上記3.が終わるまでSELECTを待つのでしょうか? DBは、MySQL5ですが、DB問わず同じ挙動と思い、 このカテゴリに質問されていただきました。

  • トランザクションの考え方

    トランザクションの考え方を教えて下さい。 「データの参照や追加・更新・削除といった処理に矛盾がないことを保証する」という記述を見たのですが、下記の場合も良いのでしょうか。 トランザクションの開始 SELECT * FROM Aテーブル WHERE 項目A = '1' --処理-- UPDATE Aテーブル SET 項目B = '2' WHERE 項目A = '1' トランザクションの終了 開始から終了までの間に他のPCによって SELECT対象のデータが増える可能性があります。 この場合UPDATEするときはやはりSELECTの結果でLOOPするべきなのでしょうか。 上記のようにするとSELECT件数とUPDATE件数は異なってしまうのでしょうか。 環境はVB2005+SQL Server です。

  • SELECT FOR UPDATE で該当レコードがなかった場合

    SELECT FOR UPDATE ですが、該当レコードのみ ロックすると思うんですが、 該当レコードがない場合は、 ロックできないんでしょうか? たとえば、(COLUMN_BBB が PK として) SELECT * FROM TABLE_AAA WHERE TABLE_AAA.COLUMN_BBB = 'BBB' FOR UPDATE で、そもそも SELECT * FROM TABLE_AAA WHERE TABLE_AAA.COLUMN_BBB = 'BBB' となるレコードがない場合でも、 他トランザクションによる 該当レコードの INSERT を排他防御できるのか無理なのか、 教えていただけたらありがたいです。 すみません、時間的余裕があまりないので、 (すぐに回答ほしいです)でアップします。

  • vb.net トランザクション処理について

    VB2005、SQLServer2005環境です。 VB.Netのトランザクション処理でエラーが発生します。 エラー内容の意味すらわかりません。 どなたかご教授お願いします。 *エラー内容 "System.InvalidOperationException: ExecuteReader は、コマンドに割り当てられた接続が保留状態である ローカルのトランザクションにあるとき、トランザクション オブジェクトを持つコマンドが必要です。 コマンドの Transaction プロパティがまだ初期化されていません。 Private Sub BTN_更新_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BTN_更新.Click Dim strsql As String Dim Tran As SqlClient.SqlTransaction = Nothing '---DB接続 Call DBConnect() Try '---SQL文作成 strsql = "SELECT * FROM Aテーブル" '---SQL文を作成して実行する Dim comm As SqlCommand = New SqlCommand(strsql, Con) '---データアダプタの作成 Dim dataadapter As SqlDataAdapter = New SqlDataAdapter(comm) '---データセットへの読み込み Dim ds As DataSet = New DataSet() dataadapter.Fill(ds, "Aテーブル") Dim dt As New DataTable dt = ds.Tables("Aテーブル") '---主キーの設定 dt.PrimaryKey = New DataColumn() {dt.Columns("Aテーブル")} '---データの更新 Dim targetRow As DataRow targetRow = dt.NewRow() targetRow = dt.Rows.Find(TXT_コード.Text) targetRow("コード") = TXT_コード.Text targetRow("氏名") = TXT_氏名.Text '---コマンド自動作成 Dim cb As SqlCommandBuilder = New SqlCommandBuilder(dataadapter) '---トランザクション開始 Tran = Con.BeginTransaction() '---データベースの更新 dataadapter.Update(ds, "社員テーブル")     <<<エラーになります。 '---トランザクション完了 Tran.Commit() Catch oExcept As Exception '---ロールバック Tran.Rollback() '例外が発生した時の処理 MessageBox.Show(oExcept.ToString, "例外発生") Finally '---DB切断 Call Disconnect() End Try

  • reindex と update のデッドロック

    実運用中のテーブルに対し、日次で reindex を実行したところ、時々、デッドロックが発生してしまいます。 どうやら、reindex と update の間でデッドロックが起きているようです。 エラーメッセージを見た感じでは、以下の状態でデッドロックになっているように読めます。 ・update がテーブルのインデックスに対するロックを取得し、テーブルに対する RowExclusiveLock を取得しようとしている ・reindex がテーブルに対するロックを取得し、テーブルのインデックスに対する AccessExclusiveLock を取得しようとしている ※ 参考までに、update は select for update してから update しています。 Postgre のバグではないかと思うのですが、デッドロックを回避する方法はありますでしょうか? (なるだけ、reindex を実行しているプロセスの方に手を入れることで対応できないかと思っています。例えば、reindexの前に事前にロックを取得する、とか・・・) よろしくお願い致します。

  • ADO接続のトランザクション処理

    SQL2005のDBに対して ADOのconnectionオブジェクトで接続しているのですが、 下記のようにトランザクションを開始し、SQLを実行し、 エラーが発生してロールバックを行っていない場合、 2個目のSQLがいつまでたってもロック状態でタイムアウトになりません。 このような場合、ADOconnectionのコマンドタイムアウトのデフォルト値30秒で タイムアウトとなり、ロールバックされないのでしょうか? 以下adoコネクションオブジェクトをConnobjと表記します Connobj.BeginTrans Connobj.Execute "Insert into AAA・・・・" ← このInsert文で失敗 Connobj.Execute "Update BBB SET ・・・・" ← このUpdateは成功 ※ロールバックもコミットも行わない  (プログラムの不具合でロールバックができなかった場合や、  アプリで異常が発生してロールバックが行えない等) お手数をおかけしますが、何かご存知の方、ご教授願います。