DBを更新するアプリケーションにつきまして への返答

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

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

投稿者 魔界の仮面弁士  (社会人) 投稿日時 2020/3/18 14:13:19
C1FlexGrid 固有の機能についてはわかりませんが、ひとまず、
DataTable 上の競合データを検出するサンプルを 3 パターンほど書いてみました。

Option Strict On
Public Class Form1
  Private ds As DataSet
  Private tbl As DataTable

  Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
    ds = New DataSet()
    tbl = ds.Tables.Add("tbl")

    ' 8 個の列を持つテーブル 
    tbl.Columns.Add("F1"GetType(Integer)).AllowDBNull = False
    tbl.Columns.Add("F2"GetType(Integer)).AllowDBNull = False
    tbl.Columns.Add("F3"GetType(Integer)).AllowDBNull = False
    tbl.Columns.Add("F4"GetType(Integer)).AllowDBNull = False
    tbl.Columns.Add("F5"GetType(Integer)).AllowDBNull = False
    tbl.Columns.Add("F6"GetType(Integer)).AllowDBNull = False
    tbl.Columns.Add("F7"GetType(Integer)).AllowDBNull = False
    tbl.Columns.Add("F8"GetType(String)).AllowDBNull = True

    'そのうち 7 つの列に一意制約を追加する 
    Dim cols() As DataColumn = "F1 F2 F3 F4 F5 F6 F7".Split().Select(Function(f) tbl.Columns(f)).ToArray()
    tbl.Constraints.Add(New UniqueConstraint("UniqueKey1", cols, False))

    '検証用のダミーデータ 
    tbl.Rows.Add(1, 1, 1, 1, 1, 1, 1, "aaa")
    tbl.Rows.Add(2, 1, 1, 1, 1, 1, 1, "bbb")
    tbl.Rows.Add(3, 1, 1, 1, 1, 1, 1, "ccc")
    tbl.Rows.Add(4, 1, 1, 1, 1, 1, 1, "ddd")
    tbl.Rows.Add(5, 1, 1, 1, 1, 1, 1, "eee")
    tbl.Rows.Add(6, 1, 1, 1, 1, 1, 1, "fff")
    tbl.Rows.Add(7, 6, 5, 4, 3, 2, 1, "ggg")
    tbl.Rows.Add(8, 7, 6, 5, 4, 3, 2, "hhh")
    tbl.Rows.Add(9, 8, 7, 6, 5, 4, 3, "iii")

    '制約を外しておく 
    ds.EnforceConstraints = False
    DataGridView1.DataSource = tbl
    DataGridView1.AutoResizeColumns()

    Button1.Text = "制約で競合検証"
    Button2.Text = "ループで競合検出"
    Button3.Text = "LINQで競合検出"
  End Sub

  Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
    Try
      ds.EnforceConstraints = True
      MessageBox.Show("エラー無し")
    Catch
      Dim sb As New System.Text.StringBuilder()
      For Each r In tbl.GetErrors()
        sb.AppendLine(r.RowError)
      Next
      MessageBox.Show("エラーあり" & vbCrLf & sb.ToString())
    Finally
      ds.EnforceConstraints = False
    End Try
  End Sub

  Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
    Dim GetKey As Func(Of DataRow, String) = Function(r) String.Join( _
         "-""F1 F2 F3 F4 F5 F6 F7".Split().Select(Function(f) CStr(r(f))))

    Dim sb As New System.Text.StringBuilder()
    Dim view As New DataView(tbl, """", DataViewRowState.CurrentRows)
    For i = 0 To view.Count - 2
      Dim key1 = GetKey(view(i).Row)
      For j = i + 1 To view.Count - 1
        Dim key2 = GetKey(view(j).Row)
        If key1 = key2 Then
          sb.AppendLine(String.Format("{0}行目と{1}行目の値が同じです。", i + 1, j + 1))
        End If
      Next
    Next
    If sb.Length = 0 Then
      MessageBox.Show("競合無し")
    Else
      MessageBox.Show(sb.ToString())
    End If
  End Sub

  Private Sub Button3_Click(sender As System.Object, e As System.EventArgs) Handles Button3.Click
    Dim GetKey As Func(Of DataRow, String) = Function(r) String.Join("-""F1 F2 F3 F4 F5 F6 F7".Split().Select(Function(f) CStr(r(f))))

    Dim Indexes()() As Integer
    Indexes = tbl.AsEnumerable() _
        .Select(Function(Row, Index) New With {Index, .Key = GetKey(Row)}) _
        .GroupBy(Function(r) r.Key, Function(r) r.Index) _
        .Where(Function(r) r.Count() > 1) _
        .Select(Function(r) r.Select(Function(Index) 1 + Index).ToArray()).ToArray()

    If Not Indexes.Any() Then
      MessageBox.Show("競合無し")
    Else
      Dim sb As New System.Text.StringBuilder()
      For Each lines In Indexes
        sb.Append(String.Join("、", lines)).AppendLine("行目が重複しています。")
      Next
      MessageBox.Show(sb.ToString())
    End If
  End Sub
End Class
投稿者 あいいうえ  (社会人) 投稿日時 2020/3/18 11:18:49
> まずは DataTable に重複キー制約を付与しておきます。(型付DataSet でも可)

大変申し訳ありませんが、どのようにコーディングをすればよいのかがわかりません。
参考サイトのURL、もしくはサンプルコードのようなものをいただければ幸いです。

また、最初の質問に記載しましたコンボリストの件は解決しました。
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2020/3/16 17:58:54
C1FlexGrid は使ったことが無く、所有もしていないため、
メーカーのサポートページを見ているだけではありますが:
https://docs.grapecity.com/help/c1/winforms/winforms_flexgrid/


> グリッドの編集結果を取り出す方法であったり
.DataSource プロパティにバインドしているのなら、
DataTable に編集結果が格納されていると思います。
もしもアンバウンドで使っているなら、.GetData メソッドで編集結果が得られるのでは
ないでしょうか。(総行数は .Rows.Count で)


> 重複チェックのアルゴリズムなど
幾つか方法がありますが、2010 なら DataSet の制約機能を使うのはどうでしょう?

まずは DataTable に重複キー制約を付与しておきます。(型付DataSet でも可)
https://docs.microsoft.com/ja-jp/dotnet/api/system.data.uniqueconstraint?view=netframework-4.8

そしてユーザーには、EnforceConstraints = False の状態で入力させておき、
重複検証を行う際に EnforceConstraints = True にします。制約違反があると
例外が発生するので、Try~Catch で補足します。

EnforceConstraints = True で、どの行が原因で制約違反になったのかを調べるには、
DataTable の GetErrors メソッドを使えます。
エラー行のうち、どの列に問題があったのかを調査するには、
DataRow の GetColumnsInError メソッドと GetColumnError メソッドを使えます。


あるいは LINQ to DataSet で Group By して、.Count() > 1 なレコードを得るという手もありそう。
投稿者 あいいうえ  (社会人) 投稿日時 2020/3/16 15:01:04
ご返信ありがとうございます。

> どの C1FlexGrid をお使いでしょうか。WinForms 版?
WinForms 版です。


> やりたいことは分かりましたが、質問点が分かりませんでした。
大雑把な説明ですいません。
C1FlexGridもDataGridViewも触りたての素人です。
Oracleには接続済みで、グリッドにデータベースの内容を表示したり、追加、削除、更新などはできています。ですので、グリッドの編集結果を取り出す方法であったり、重複チェックのアルゴリズムなどがわからないというのがお答えするべきことなのかと思います。

> 要件によって、幾つかの実装パターンが考えられますね。
行自体は十数件しかないので(1)~(2)辺りが適していると思います。

> どのタイプのコンボをお使いですか?
' 1列目:組み込みコンボ(非連結コンボ)だと思います。


質問に正しく答えられていないかもしれません。
もし至らぬ点があればわかる範囲でお答えします。

投稿者 魔界の仮面弁士  (社会人) 投稿日時 2020/3/16 12:09:47
> その内容をC1flexgrid(サードパーティ製のdatagridviewの様なもの)をいじることで更新できるアプリケーションを作ろうとしています。
どの C1FlexGrid をお使いでしょうか。WinForms 版?

https://docs.grapecity.com/help/c1/winrt-xaml/winrtxaml_reference/C1.Xaml.FlexGrid~C1.Xaml.FlexGrid.C1FlexGrid.html
https://docs.grapecity.com/help/c1/xaml/xaml_flexsheet/html/T_C1_WPF_FlexGrid_C1FlexGrid.htm
https://docs.grapecity.com/help/c1/winforms/winforms_flexgrid/#C1.Win.C1FlexGrid.4.5.2~C1.Win.C1FlexGrid.C1FlexGrid.html


> 以下のような機能を実装しようとしているのですがどうすれば良いのか見当もつきません。
やりたいことは分かりましたが、質問点が分かりませんでした。

何処までわかっていて、どの部分が分からないのかが曖昧なので、
全部に答えようとすると、質問というよりも「作成依頼」になってしまいます。

・C1FlexGrid の使い方は習得済みですか? それとも C1FlexGrid に関する質問ですか? (DataGridView であれば実装できるのでしょうか?)
・Oracle に接続することはできていますか? 何を使って接続していますか? ODP.NET Managed Driver?
・Oracle からデータを取り出すことはできていますか? それをグリッドに表示できますか?
・Oracle にデータを書き戻すことはできていますか? あるいはグリッドの編集結果を取り出す方法が分からない?
・基本的な読み書きは出来ているけれれど、重複チェックのアルゴリズムが思いつかないので相談したいという話ですか?


> ・主キーを付けていないため、

Oracle なら、ROWID 疑似列を使うことで、疑似的なキーの代わりにできます。
https://sql-oracle.com/?p=1593

しかし、7 カラムの組み合わせを一意にしておきたいのであれば、
あらかじめ 7 カラムに対して UNIQUE INDEX を付与しておくべきかと思います。



> 7カラム全て同じ内容にした行を複数行追加できないようにしたい。
要件によって、幾つかの実装パターンが考えられますね。

(1) 競合する行データがあった場合に、行追加を拒絶する。
 (行単位で判定。数件程度の小容量データには良いが、大量の入力の際には不向き)

(2) とにかく好きなようにデータを打たせてから、サーバーに反映させる前の
 更新チェックの段階で、競合する行を通知して修正させる。
 (複数件一括で判定。Excel 等のスプレッドシート系に近い設計手法)

(3) 競合するデータを見つけたら文字色や背景色を切り替えて通知し、
 さらにサーバーに反映させる前の更新チェックも行う。(上記 1 と 2 の混合案)

(4) 画面側でチェックするだけではなく、同時に他のユーザーが同じデータを
 既に登録していた場合のチェックが漏れるため、とにかく一度、実際に
 サーバーに MERGE してみて重複チェックを行い、同一レコードが
 複数登録されていたら、Rollback した上で競合通知メッセージを表示する。


> ・7カラムあるうち一つの項目のみコンボリストで「該当」「非該当」の二つのみ選べるようにしているのですが、
どのタイプのコンボをお使いですか?
https://dev.grapecity.co.jp/support/kb/detail.asp?id=82596

> 該当なら0、非該当なら1とDBに反映させたい。
連結コンボなら ValueMember、非連結なら SelectedIndex を利用すれば
0、1 という値を得られるかと思います。そこまでできれば、
後はテキストや数値項目を更新する場合と同じ手順で良いと思います。
投稿者 (削除されました)  () 投稿日時 2020/3/16 12:01:15
(削除されました)
投稿者 あいいうえ  (社会人) 投稿日時 2020/3/16 09:48:39
お世話になります。
私は現在visualstudio2010を使ってOracleのデータベースに接続をして、その内容をC1flexgrid(サードパーティ製のdatagridviewの様なもの)をいじることで更新できるアプリケーションを作ろうとしています。
カラムは7行あり、以下のような機能を実装しようとしているのですがどうすれば良いのか見当もつきません。

・主キーを付けていないため、7カラム全て同じ内容にした行を複数行追加できないようにしたい。
・7カラムあるうち一つの項目のみコンボリストで「該当」「非該当」の二つのみ選べるようにしているのですが、該当なら0、非該当なら1とDBに反映させたい。

お力添えいただければ幸いです。