- PR -

複数テーブルのUPATE

1
投稿者投稿内容
chiroru
ベテラン
会議室デビュー日: 2008/10/02
投稿数: 59
投稿日時: 2009-03-04 13:17
言語:Microsoft Visual Studio 2008
データベース:SQL Server 2005
いつもお世話になります。複数のテーブルの更新処理を行いたいのですが、下記のように
記述すると支店テーブルしか更新されません。
テーブルは銀行テーブル(T_GINKO)と支店テーブル(T_SHITEN)があります。
<テーブル構造>
銀行テーブル           支店テーブル
GIN_CD  GIN_MEI      GST_GIN_CD  GST_CD  GST_MEI
0001    あああ銀行     0001         001       ○○支店
0002    いいい銀行     0001         002       △△支店
複数のテーブルを更新する場合どうすればいいのでしょうか?
ご教授お願いします。

Private Function Data_Update() As String
  Dim sqlConn As SqlConnection = Nothing
  Dim sqlTrans As SqlTransaction = Nothing
  Dim strsql As String
  Dim I As Integer
  Try
    Dim sqlDA1 As SqlDataAdapter = New SqlDataAdapter()
    Dim sqlDA2 As SqlDataAdapter = New SqlDataAdapter()
    Dim ds1 As DataSet = New DataSet()
    Dim ds2 As DataSet = New DataSet()
    Dim sqlCmdBldr1 As SqlCommandBuilder = New SqlCommandBuilder(sqlDA1)
    Dim sqlCmdBldr2 As SqlCommandBuilder = New SqlCommandBuilder(sqlDA2)
    sqlDA1.SelectCommand = New SqlCommand()
    sqlDA2.SelectCommand = New SqlCommand()
    '<--- 銀行テーブル更新 --->
    strsql = "SELECT * FROM T_GINKO"
    sqlDA1.SelectCommand.CommandType = CommandType.Text
    sqlDA1.SelectCommand.CommandText = strsql
    sqlDA1.SelectCommand.Connection = New SqlConnection(My.Settings.MyAppConnect)
    sqlConn = sqlDA1.SelectCommand.Connection
    sqlConn.Open()
    sqlTrans = sqlConn.BeginTransaction(IsolationLevel.Serializable)
    sqlDA1.SelectCommand.Transaction = sqlTrans
    sqlDA1.Fill(ds1, "GINKO")
    Dim newRow As DataRow
    newRow = ds1.Tables("GINKO").NewRow
    newRow.Item("GIN_CD") = "0001"
    newRow.Item("GIN_MEI") = "あいう銀行"
    ds1.Tables("GINKO").Rows.Add(newRow)
    sqlCmdBldr1.GetUpdateCommand.Connection = sqlConn
    sqlCmdBldr1.GetUpdateCommand.Transaction = sqlTrans
    '<--- 支店テーブル更新 --->
    strsql = "SELECT * FROM T_SHITEN"
    sqlDA2.SelectCommand.CommandType = CommandType.Text
    sqlDA2.SelectCommand.CommandText = strsql
    sqlDA2.SelectCommand.Connection = New SqlConnection(My.Settings.MyAppConnect)
    sqlConn = sqlDA2.SelectCommand.Connection
    sqlConn.Open()
    sqlTrans = sqlConn.BeginTransaction(IsolationLevel.Serializable)
    sqlDA2.SelectCommand.Transaction = sqlTrans
    sqlDA2.Fill(ds2, "SHITEN")
    Dim newRow As DataRow
    newRow = ds2.Tables("SHITEN").NewRow
    newRow.Item("GST_GIN_CD") = "0001"
    newRow.Item("GST_CD") = "001"
    newRow.Item("GST_MEI") = "○○支店"
    sqlCmdBldr2.GetUpdateCommand.Connection = sqlConn
    sqlCmdBldr2.GetUpdateCommand.Transaction = sqlTrans
    ' --- DataSet に対する更新結果をDB へ反映 ---
    sqlDA1.Update(ds1, "GINKO")
    sqlDA2.Update(ds2, "SHITEN")
    ds1.Dispose()
    ds2.Dispose()
    sqlTrans.Commit()
  Catch ex As IndexOutOfRangeException
    sqlTrans.Rollback()
    MessageBox.Show("既に削除された可能性があります", AppName, MessageBoxButtons.OK)
  Catch ex As Exception
    sqlTrans.Rollback()
    AppErrorHandler(ex)
  Finally
    If Not sqlTrans.Connection Is Nothing Then
     sqlTrans.Rollback()
    End If
    If Not (sqlConn.State = ConnectionState.Closed) Then
     sqlConn.Close()
    End If
  End Try
End Function
デューン
大ベテラン
会議室デビュー日: 2004/04/21
投稿数: 174
お住まい・勤務地: Tokyo
投稿日時: 2009-03-04 15:52
ざっとしか見てませんが、
銀行テーブルのupdateしてcommitする前にsqlTransに
別のコネクションの別のトランザクションをセットしてるからじゃないですか?
chiroru
ベテラン
会議室デビュー日: 2008/10/02
投稿数: 59
投稿日時: 2009-03-04 16:17
引用:

デューンさんの書き込み (2009-03-04 15:52) より:
ざっとしか見てませんが、
銀行テーブルのupdateしてcommitする前にsqlTransに
別のコネクションの別のトランザクションをセットしてるからじゃないですか?


銀行テーブル1件に付き、支店テーブル1件は必ず存在するため、
支店テーブルでエラーが起きた場合、銀行テーブルの更新も支店テーブルの
更新もロールバックしたいと思っています。
この場合どうすればいいのでしょうか?

デューン
大ベテラン
会議室デビュー日: 2004/04/21
投稿数: 174
お住まい・勤務地: Tokyo
投稿日時: 2009-03-04 19:20
うまくいかないのは、変数を使いまわしている部分があるためです。


片方だけ追加される状況を避けたいのだと思いますが
今回のケースではコネクションもトランザクションも1つにしたほうがわかりやすいかもしれません。

chiroru
ベテラン
会議室デビュー日: 2008/10/02
投稿数: 59
投稿日時: 2009-03-04 19:39
すみません。
>今回のケースではコネクションもトランザクションも1つにしたほうがわかりやすい>かもしれません。
の意味をもう少し教えて下さい。
かずい
常連さん
会議室デビュー日: 2008/09/30
投稿数: 28
お住まい・勤務地: 北海道札幌市
投稿日時: 2009-03-04 20:18
こんばんは、かずいです。

引用:

sqlDA1.SelectCommand.Connection = New SqlConnection(My.Settings.MyAppConnect)
sqlConn = sqlDA1.SelectCommand.Connection
sqlConn.Open()
sqlTrans = sqlConn.BeginTransaction(IsolationLevel.Serializable)

<中略>

sqlDA2.SelectCommand.Connection = New SqlConnection(My.Settings.MyAppConnect)
sqlConn = sqlDA2.SelectCommand.Connection
sqlConn.Open()
sqlTrans = sqlConn.BeginTransaction(IsolationLevel.Serializable)
sqlDA2.SelectCommand.Transaction = sqlTrans



この中でコネクションとトランザクションが2回発行されているので、これを
sqlconn = new sqlconnection(My.Settings.MyAppConnect)
sqlconn.open
sqltrans = sqlConn.BeginTransaction(IsolationLevel.Serializable)
sqlDA1.SelectCommand.Connection = sqlconn
sqlDA1.SelectCommand.Transaction = sqlTrans

sqlDA2.SelectCommand.Connection = sqlconn
sqlDA2.SelectCommand.Transaction = sqlTrans

見たいな感じにすればいいという感じだと思います。
コネクションとトランザクション、コミットの関係をきちんと整理して
おいた方が良いと思いますよ。
基本的にはDB更新時にはコネクションとトランザクションの開始、コミットは1つづつにした方が良いです。

あ、このソースは単純に並び換えただけなので、このままで動くかどうかは検証していません。
デューン
大ベテラン
会議室デビュー日: 2004/04/21
投稿数: 174
お住まい・勤務地: Tokyo
投稿日時: 2009-03-05 00:40
かずいさんが既に解説してくれていますが、


コネクションが複数あるということは、
「別の接続」という事ですので、それは別の人が接続しているのと同じような状況になります。

同様にトランザクションが複数あるということは
「別のトランザクション」という事ですので、銀行の処理と支店の処理が独立したトランザクションになってしまいます。
これは言ってみれば銀行の更新処理と支店の更新処理が別々に管理されてしまう事になってしまいます。

関連のある更新処理であれば「ひとつのトランザクション」というくくりにまとめて、
失敗した場合はどちらで失敗したにせよ、その「ひとつのトランザクション」をまるごとロールバックすれば、
そのトランザクションに含まれている更新処理を全部なかったことにする。
という手法もとれるということです。

かずいさんも「基本的に」とおっしゃっているように例外はありえますが、
今回ぐらいの2テーブルに1件ずつということであれば、1トランザクションでいいでしょう。
chiroru
ベテラン
会議室デビュー日: 2008/10/02
投稿数: 59
投稿日時: 2009-03-05 09:43
ありがとうござます。
コネクションとトランザクションを1つのくくりにすることでうまくいきました。
1

スキルアップ/キャリアアップ(JOB@IT)