- PR -

外部結合させて取り込んだdatasetをDBにUpdateする方法

投稿者投稿内容
ポートス
会議室デビュー日: 2006/03/03
投稿数: 3
投稿日時: 2006-03-03 01:19
初めての書き込みです。
このサイトやmsdnのサイトを調べてみたのですが、探しきれませんでしたので投稿させて頂きます。
皆様よろしくお願い致します。

開発環境は、以下となっております。
OS:Windows XP
DB:Oracle 10g
IDE:VB.NET 2003

2つのテーブル(TBLHEAD, TBLBODY)を外部結合させてdatasetに取り込み、取り込んだデータをフォームのテキストボックスに表示させています。テキストボックスのデータを編集して「更新」ボタンを押せばDBに更新されるようにしたいのですが、2つのテーブルを更新させる方法が分かりません。方針としては、OracleDataAdapterのUpdateメソッドを使って、一旦、TBLHEAD と TBLBODY を DELETE させた後、INSERT するようにしたいです。
今回は、DBの2つのテーブルを DELETE させる方法を教えて下さい。

なお、TBLHEAD には SHAINCD(社員コード)、SHAINNAME(社員名)の列があり、SHAINCD が主キーです。SHAINCD=1 のものは、1件です。
TBLBODY には SHAINCD(社員コード)、SHAINKBN(社員区分)、SHAINADD(社員住所)の列があり、SHAINCD と SHAINKBN が主キーです。SHAINCD=1のものは、10件あります。
dataset には、SHAINCD=1 のものが 10レコード取り込まれています。

下記ソースだと、@TBLBODY の10件分を DELETE することには成功していますが、ATBLHEADの1件を DELETE することができません。
2つのテーブルの SHAINCD=1 のものを DELETE する方法を教えて下さい。

ソースの一部を下記に添えておきます。

'-----以下ソース-----
Private dataAda As New OracleDataAdapter
Private cnn As New OracleConnection
Private cmnd As New OracleCommand
Private dataset As New dataset

cnn.ConnectionString = "Data Source = " & "(SERVICE NAME)" & "; " & _
"User ID = " & "(USER ID)" & "; " & _
"Password = " & "(PASSWORD)" & ";"
'接続の確立
cnn.Open()
'接続済みのコネクションを指定
cmnd.Connection = cnn
dataAda.SelectCommand = New OracleCommand(CreateSQL, cnn)
'DataSetの準備(DataTableオブジェクトの作成)
dataset.Tables.Add("TBL")
'DataSetへの充填
dataAda.Fill(dataset.Tables("TBL"))
Dim datatbl As DataTable = dataset.Tables("TBL")
'主キーを設定
datatbl.PrimaryKey = New DataColumn() {datatbl.Columns("SHAINCD")}

'データセットの全行(10件)を削除
For i As Integer = 0 To datatbl.Rows.Count - 1
datatbl.Rows(i).Delete()
Next

'@-----TBLBODYの更新-----
dataAda.DeleteCommand = New OracleCommand("DELETE FROM TBLBODY " & _
"WHERE(SHAINCD = :SHAINCD)"
"AND(SHAINKBN = :SHAINKBN)", cnn)
With dataAda.DeleteCommand
'社員コード
Dim param1 As OracleParameter = .Parameters.Add("oldSHAINCD", OracleDbType.Int16)
param1.SourceVersion = DataRowVersion.Original
param1.SourceColumn = "SHAINCD"
'社員区分
Dim param2 As OracleParameter = .Parameters.Add("oldSHAINKBN",OracleDbType.Int16)
param2.SourceVersion = DataRowVersion.Original
param2.SourceColumn = "SHAINKBN"
End With

'UpdateメソッドでDBに書き戻す
dataAda.Update(dataset.Tables("TBL"))


'A-----TBLHEADの更新-----
dataAda.DeleteCommand = New OracleCommand("DELETE FROM TBLHEAD " & _
"WHERE(SHAINCD = :SHAINCD)", cnn)
With dataAda.DeleteCommand
'社員コード
Dim param1 As OracleParameter = .Parameters.Add("oldSHAINCD",OracleDbType.Int16)
param1.SourceVersion = DataRowVersion.Original
param1.SourceColumn = "SHAINCD"
End With

'UpdateメソッドでDBに書き戻す
dataAda.Update(dataset.Tables("TBL"))

'-----ソースここまで-----


 不足な点があればご指摘頂けれると幸いです。よろしくお願い致します。
せん
ぬし
会議室デビュー日: 2002/03/04
投稿数: 397
投稿日時: 2006-03-03 09:03
引用:

ポートスさんの書き込み (2006-03-03 01:19) より:

下記ソースだと、@TBLBODY の10件分を DELETE することには成功していますが、ATBLHEADの1件を DELETE することができません。



なぜ、出来ないのでしょうか。
エラー等が発生しているのならばその旨書いていただかないと他人には伝わりません。
ポートス
会議室デビュー日: 2006/03/03
投稿数: 3
投稿日時: 2006-03-03 10:17
せんさんご回答有難うございます。
エラーは発生しません。
@の「UpdateメソッドでDBに書き戻す」では、TBLBODYのSHAINCD=1の10件が削除されますが、
Aの「UpdateメソッドでDBに書き戻す」では、TBLHEADのSHAINCD=1が削除されません。
デバッグ実行してもその行ではエラーは出ません。

なお、@とAの処理を逆にした場合、つまり
'A-----TBLHEADの更新-----
'@-----TBLBODYの更新-----
の順にコードを書き換えた場合、
AのdataAda.Update(dataset.Tables("TBL"))で以下のエラーが出ます。

'System.Data.DeletedRowInaccessibleException'のハンドルされていない例外が
system.data.dll で発生しました。
追加情報 : 削除された行を通して、その行の情報にアクセスすることはできません。

このエラーを回避するために、
For i As Integer = 0 To datatbl.Rows.Count - 1
 datatbl.Rows(i).Delete()
Next
の3行を
 datatbl.Rows(0).Delete()
に書き換えると、
dataAda.Update(dataset.Tables("TBL")) が走った時点でTBLHEADの SHAINCD=1 が削除されますが、2回目の
dataAda.Update(dataset.Tables("TBL")) が走ってもTBLBODYの SHAINCD=1 が削除されません。エラーも出ません。

よろしくお願い致します。
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2006-03-04 22:20
 ソースコードをそのまま書くのではなく、コードが何をしているかを、書いてもらえないですか?

 もっとも、そうすれば、ほとんど自力で解決できるのですが。


 つまり、コードが何をしているか理解していないから、わからないのです。

 10行消していますが、10行消したテーブルで、別のテーブルを消そうとしています。もう消えているんだから、消せるわけがありません。


'データセットの全行(10件)を削除
For i As Integer = 0 To datatbl.Rows.Count - 1
datatbl.Rows(i).Delete()
Next
----- DataTable.Rows(index).Delete の呼び出しは、ここだけ

'UpdateメソッドでDBに書き戻す
dataAda.Update(dataset.Tables("TBL"))
----- ここで、TBL テーブルをアップデートしている


'UpdateメソッドでDBに書き戻す
dataAda.Update(dataset.Tables("TBL"))
----- そしてまた、DataTable を操作せずに、TBL テーブルをアップデートしている



 あ〜、「外部結合」って、“どこで”やってます?ちゃんとデータベース側でやってますか?
 きちんとそうして、CASCADE しているなら、TBLHEAD の1つを消せば、TBLBODY の10行は消えますよね。
 何かの都合でそうしていないなら、TBLBODY を消し、参照しているものがなくなってから、TBLHEAD を消さなければなりません。そうであるなら、2つのテーブルを DataSet に取り込まないといけませんよ。

〆 written by Jitta@わんくま同盟 on 2006/03/04

[ メッセージ編集済み 編集者: Jitta 編集日時 2006-03-04 22:25 ]
長老
会議室デビュー日: 2005/12/10
投稿数: 3
投稿日時: 2006-03-04 22:29
興味があり調べてみたのですが、DataAdapterでの変更は1テーブルに対するようデザインされておりジョインされたテーブルに対して行うのはやめたほうがいい、という結論になりました。
Updateメソッドはデータセットの変更を実テーブルに反映するためのメソッドと考えられますから、提示されているコードを単純に読んでみただけでもUpdateメソッドを別々のテーブルに対して2度呼んでいる時点でうまくいかない気がします。

参考にしたのは以下のページです。
http://dotnet247.com/247reference/msgs/47/238210.aspx
http://msdn.microsoft.com/library/ja/default.asp?url=/library/ja/cpguide/html/cpconupdatingdatabasewithdataadapterdataset.asp
http://www.microsoft.com/japan/msdn/net/bda/bdadotnetdata4.asp

1番目のアドレスのレスにあるようにそれぞれの実テーブルを別々のDataTableに分けて取得しそれぞれを更新or削除するようにしたほうが無難ではないでしょうか。あるいはパフォーマンスを無視できるならまず実テーブルに実際にDeleteコマンドを実行し結果を再度読み込むとすれば修正量が少なくてすむかも知れませんね。

逆に質問ですが提示されているコードでいけるのではと考えた根拠を教えていただきたいなと思います。それと、もともと更新するのが最初の意図のようですがあえてまず削除のコードを提示しているのはなぜですか。
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2006-03-04 22:53
 ってか、ID 指定で削除するなら、DbCommand で DELETE 文を発行すればすむ話で、それだと2回のラウンドトリップですむ。

 DbDataAdapter を使うために、SELECT して Delete して Update で DELETE していたら、4回のラウンドトリップが必要。「全行削除」ってことは、Fill で持ってくるデータは削除するためだけのデータで、反対に考えれば、そのまま DELETE すればいい、ってこと。
ポートス
会議室デビュー日: 2006/03/03
投稿数: 3
投稿日時: 2006-03-08 01:38
Jittaさん、長老さん、ご回答有難うございます。また、お返事を差し上げるのが遅くなり、大変申し訳ありませんでした。

----------
>Jittaさん
>「外部結合」って、“どこで”やってます?ちゃんとデータベース側でやってますか?
 VBのコード中に、メソッド(Private Function)として外部結合してSELECTさせるSQLを記述しています。
 そのSQL文を文字列として、
dataAda.SelectCommand = New OracleCommand(CreateSQL, cnn)
の CreateSQL に格納させています。
 「DB側で」とおっしゃる意味がよく分からないのですが、以上の解答で構いませんでしょうか。

>2つのテーブルを DataSet に取り込まないといけませんよ。
 ということは、TBLHEAD と TBLBODY の2つを、別々の DataSet に取り込まないといけないのでしょうか?

>ID 指定で削除するなら、DbCommand で DELETE 文を発行すればすむ話
 DbCommand を使った方法を、調べてみようと思います。有難うござます。

----------
>長老さん
 海外のサイトまでご覧になって調べて下さったのですね…。有難うございます。

>別々のDataTableに分けて取得
 処理コードの簡略化を考えておりましたので、1つの DataTable を使った方法があればと思ってました。

>提示されているコードでいけるのではと考えた根拠
 2つのテーブルを外部結合させた場合の例を探しきれませんでしたので、私の勝手な推測でコーディングしておりました。根拠は特にございません。
 ただ、1つのテーブルの場合、下記のPC書籍
http://www.shoeisha.com/mag/windev/library/870510.asp
「Accessユーザーのための早わかりADO.NET」のコーナーを参考に致しました。

>まず削除のコードを提示しているのはなぜですか。
 このBBSに提示するソースが長くなり、読みづらくなると考えたからです。分けたことで話が見えにくくなってしまった点はお詫び致します。
鎌田
常連さん
会議室デビュー日: 2003/09/23
投稿数: 45
投稿日時: 2006-03-08 11:21
OracleCommandは、DataAdapterにDeleteCommandやUpdateCommandとして結びつけなくても、独立してSQL文の発行に使えます。
ボタン押下時のみデータベース更新処理を行うなら、DataAdapterにはDeleteCommandやUpdateCommandを設定せず、ボタン押下のイベントハンドラで、OracleCommandを使い自力で「必要な数だけ」UPDATE文やDELETE文を発行すればOKです。

UPDATE文やDELETE文の対象になるデータ行をDataSetから取得するのはGetChangesメソッドが使えます。


複数行データをDataGridなどグリッド系コントロールではなく、テキストボックスに表示されているとのことなので、どんな画面レイアウトでどのようにDataSetから表示しているのか想像が付きませんが、下記※のデータ構造で複数行でばらばらに社員名を変更されたときに社員名を何に書き換えるかを決定できるかの方が問題に思えます。(社員名欄が一つしかないならTBLHEADを結合させる必要はないはずですし)

※>なお、TBLHEAD には SHAINCD(社員コード)、SHAINNAME(社員名)の列があり、SHAINCD が主キーです。SHAINCD=1 のものは、1件です。
TBLBODY には SHAINCD(社員コード)、SHAINKBN(社員区分)、SHAINADD(社員住所)の列があり、SHAINCD と SHAINKBN が主キーです。SHAINCD=1のものは、10件あります。
dataset には、SHAINCD=1 のものが 10レコード取り込まれています。

加えてサンプルコードのような主キー定義をしておきながら、"SHAINCDとSHAINKBN が主キーである"TBLBODYのデータを10件取り込めるところが不思議でなりません。

>Dim datatbl As DataTable = dataset.Tables("TBL")
>'主キーを設定
>datatbl.PrimaryKey = New DataColumn() {datatbl.Columns("SHAINCD")}



JOINを使い1回で取得するのではなく、TBLHEADとTBLBODYを別個のDataTableに取得し、更新時はトランザクション内でTBLHEADとTBLBODYを更新する処理が合理的に思えます。

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