DataTableの差分取得方法 への返答

投稿で使用できる特殊コードの説明。(別タブで開きます。)
本名は入力しないようにしましょう。
投稿した後で削除するときに使うパスワードです。返答があった後は削除できません。
返答する人が目安にします。相手が小学生か社会人かで返答の仕方も変わります。
最初の投稿が質問の場合、質問者が解決時にチェックしてください。(以降も追加書き込み・返信は可能です。)
※「過去ログ」について書くときはその過去ログのURLも書いてください。

以下の返答は逆順(新しい順)に並んでいます。

投稿者 shu  (社会人) 投稿日時 2017/9/4 16:17:03
処理内容的に可能かわかりませんが
更新する前に差分を取得しておけば出来ませんでしょうか?

サンプル:
        Dim tbl As New DataTable

        With tbl.Columns
            .Add("Col1"GetType(Integer))
            .Add("Col2")
        End With

        For i = 0 To 10
            tbl.Rows.Add(i, i.ToString)
        Next
        Dim rows1 = tbl.Select("""", DataViewRowState.ModifiedCurrent Or DataViewRowState.Added)
        tbl.AcceptChanges()

        For i = 0 To 10 Step 2
            tbl.Rows(i)("Col2") = (i * 3).ToString
        Next
        Dim rows2 = tbl.Select("""", DataViewRowState.ModifiedCurrent Or DataViewRowState.Added)
        tbl.AcceptChanges()

        Console.WriteLine("rows1:")
        For Each row In rows1
            Console.WriteLine("{0} {1}", row("Col1"), row("Col2"))
        Next

        Console.WriteLine("rows2:")
        For Each row In rows2
            Console.WriteLine("{0} {1}", row("Col1"), row("Col2"))
        Next

投稿者 魔界の仮面弁士  (社会人) 投稿日時 2017/8/31 19:29:17
概念論でしか述べられない質問内容なので、なかなか答えにくいところですが:


> ②検索条件入力部に条件を入力し、フォーム上の検索ボタン押下
> ③条件のSQLが発行され、DBから取得した検索結果が画面に表示される
> ②③の処理を再度実行しているのですが、あまりにも再表示に時間がかかる為、


データ取得がどのような形で行われているのか、データ表示手順はどのようなものなのか。
どう見直すべきかは、実際のコードを見ないとアドバイスできませんので、掲示板上では
一般論でしか言えませんが、今回の場合、「SQL の実行」に時間がかかるのか、
DataTable 取得後の表示処理に、どの程度の時間がかかるのかを調査すべきかと思います。

また、検索条件によって取得されるデータ量が多いときと少ないときで、
どの程度の時間差が生まれるのかも調査してみてください。


データ量の差で大きな時間差があるようであれば、差分処理によって
「必要最小限」のデータのみを取得するようにすることで、
処理時間を軽減することができるかもしれません。
(逆に、差分確認部分がボトルネックになって、悪化する可能性も無いとはいえませんが)

一方、データ量の差による違いがそれほどでもなく、
SQL の実行時間の方が圧倒的に長いようなケースであれば、
それはデータベース側のチューニングを施すのが先であり、
VB 側の処理を見直したところで根本解決にはならないと思います

あるいは、そもそも DataTable を使うことがボトルネックになっていて、
ファイアホースな DataReader で得た情報を Dictionary に保持したほうが
高速になるといったケースさえあります。


―――ということで、パフォーマンスを見直す場合には、まずは
コードのどの行がボトルネックになっているのかを測定するのが肝要です。

基本的にはもっとも時間がかかっているところから見直すのが定石ですが、
一行一行の処理時間が一瞬だったとしても、それがループ処理の内側にあるものなら、
僅かな高速化でも効果があるかもしれません。ケースバイケースですよね。



> 現在、④の更新後、⑤で更新内容を画面に再表示させる為に、

そもそも、④の更新はどのように行われているのでしょうか。
ストアドプロシージャーなのか、アドホッククエリーなのか。
一行単位での更新なのか、複数レコードに対する一括 Update な SQL なのか。

前提条件が分からないので、どう差分を取るかも答えにくいですが、
とりあえず差分判定ということなら:

案1)更新結果を一時テーブルに保持しておき、サーバー側で差分 SQL を発行する。(EXCEPTなど)

案2)影響を受けるレコードが限定されているなら、ファイアホースな DataReader で
 取り出して、クライアント側で順次探索して表示に反映していく。

案3)2 つの DataTable を比較するのなら、行数が同じなら SequenceEqual、
 削除や追加がありえるなら、Linq 等で Dictionary 化して付きあわせる。
 http://temochic.com/?p=81
 https://social.msdn.microsoft.com/Forums/ja-JP/ced9e34b-6df8-46f7-a09e-d63483758e7b/datatable



で、データベース側で確認するところとしては:

・実行プランからボトルネックを探ってみる。
 たとえば、Index Seek 後のキー参照が高コストになっている場合は、
 付加列インデックスの利用を検討してみるなど。


VB との連携で考えてみるなら:

・結合テーブル数が多いと時間がかかるが、テーブル数が少なければ十分早い場合は、
 結合度を減らした別々の問い合わせに分解して、まとめて DataSet に蓄えてから、
 VB 側で DataRelation あるいは LINQ で結合することをケントウするなど。
投稿者 パンケーキ  (社会人) 投稿日時 2017/8/31 00:22:55
魔界の仮面弁士様

早々にご回答いただきありがとうございました。
誤解を招く表現があった為、以下に、現在作成中の画面処理について、説明を補足させていただきます。

・システム構成
Windows10Pro+SQLSERVER2012

・処理の概要
①検索画面が起動

②検索条件入力部に条件を入力し、フォーム上の検索ボタン押下

③条件のSQLが発行され、DBから取得した検索結果が画面に表示される
※この検索結果データを、データテーブルで保持しています。

④画面を更新
※ここで、画面の更新内容をDBに反映します。

⑤更新結果を表示

現在、④の更新後、⑤で更新内容を画面に再表示させる為に、
②③の処理を再度実行しているのですが、あまりにも再表示に時間がかかる為、
更新後にDBからデータをデータテーブルに取得し、それをそのまま再表示するのではなく
③で保持している更新前のデータテーブルと比較し、その差分のみ再表示しようと考えた訳です。

あと、データテーブルは、画面に表示される項目とはバインドしていません。

以上、状況について説明を補足させていただきました。
お手数ですが、引き続き、ご指導ご教示の程、よろしくお願い致します。
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2017/8/30 17:29:44
> その差分データだけ、画面に反映させたいと考えています。

「クライアントで編集した部分」だけをリフレッシュしてしまうと、
他のユーザーによって編集されていたレコードが、古いままになりませんか?

ですから画面表示をリフレッシュする場合には、編集した部分だけを再取得するのではなく、
最初と同じ問い合わせを再度投げなおす方が良いと思います。
DataGridView 等を使っていた場合は、以前と同じ位置が表示されるよう、
スクロール位置の調整なども行っておくと丁寧かも。


あるいは、ページング処理を行っていた場合には、全レコードを読み直すのではなく、
現在表示していたページのレコードのみを読み直すという選択肢もあります。
とはいえその場合でも、他ユーザーの編集によって総レコード数が増減している場合、
現在のページ数が変化している可能性がありえますので、それを考慮した
画面設計が必要になりますね。
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2017/8/30 17:14:44
DataTable を編集した場合、GetChanges メソッドを呼ぶことで、
編集したデータのみを差分情報として取り出すことができます。

後はそれを、まるごと DataAdapter.Update に渡せば OK です。

削除済みレコードであれば DeleteCommand を通じて DELETE され、
編集済みレコードであれば UpdateCommand を通じて UPDATE され、
追加されたレコードならば InsertCommand を通じて INSERT されます。

実際には、GetChanges を呼ばずに直接 DataAdapter.Update に渡してもかまいません。
(未編集の行は単に無視されます)

なお、DataAdatper.Update に渡す前に AcceptChanges / RejectChanges を
呼び出してしまうと、これらの編集情報はリセットされますのでご注意ください。

http://okwakatta.net/code/ado09.html
https://msdn.microsoft.com/ja-jp/library/ww3k31w0.aspx
投稿者 パンケーキ  (社会人) 投稿日時 2017/8/30 16:21:25
VS2015を使用してWindowsアプリの開発をしている初級程度のPGです。

やりたい事は、
データ更新処理前に、画面表示用に取得していたDataTableと、データ更新後に取得したDataTableの差分を取得し、その差分データだけ、画面に反映させたいと考えています。
(データ更新の度に、全てのデータを再表示させると、件数が多く、非常に時間がかかりますので)

一応自分でも調べたのですが、
更新前のDataTableの主キーで、更新後のDataTableを
データ件数分、selectしていくくらいしか思いつきません。

何が良い方法はないでしょうか?
ご指導の程、よろしくお願いいたします。