トップ回答者
datagridviewから値入力時の重複値チェック方法について

質問
-
よろしくお願いいたします。
[サーバー]:OS Windows Server 2003 Std + SQL Server 2008 r2
[クライアント]:VS C# 2010 pro + Windows 7 pro
複数のPCでFormのdatagridviewにてDBの値を表示・編集しています。
内容は
r_id IDENTITY
id int
results DATETIME
n_id int
s_id int
time_stamp DATETIME
です。
操作の流れとしては
フォームに設置したコンボボックスcmb_n_idで番号(n_id)を選ぶ
↓
するとリレーションされた同じくフォームに設置したコンボボックスcmb_s_idで番号(s_id)を選ぶ
↓
番号n_idとs_id絞り込まれて抽出されたレコードが一覧表示される
↓
新規入力なら
id に番号を入力
results に時間を入力
↓
更新なら
既存のidのresultsカラムの値を書き換えて
↓
更新ボタンを押すと反映されるという仕組みです。
ここで問題なのですが、新規入力の際、idに値を入力する時に
重複する番号があればなんらかの方法で警告なり処理を止めるか戻すなりしたいのですが
DBのトリガなどでINSERT時にチェックさせるようにすればと考えましたが、それだと更新ボタンを押すまで反映というこの構造では
間違って同じidを新規入力していれば、更新に失敗するのではと考えました。
1レコードの場合ならまだしも何百レコードの場合もあることから、このような動作では更新そのものが失敗するので
また一から再度入力しなければならないのかと思いました。
このような場合、idカラムの入力時にselect count(id) ... のようなクエリを走らせ値が1以上ならエラーを返し、0ならINSERT INTO...という
方法でチェックするという方法なのでしょうか?それともこんな方法ではない定石があるのでしょうか?
どなたかご指導お願いします。
- 編集済み oira3ryu 2011年6月12日 2:25 タイトルから内容が分かりずらかったため
回答
-
ユーザーがidを入力するのであれば、実際にデータベースに保存するまで重複するかどうかはわかりません。よって、失敗する可能性は常に残るということです。失敗した時にどう扱うかはアプリケーションの仕様によります。何百と更新する場合、一つでも登録に失敗すれば全ての登録を取り消すか、登録できたものはそのまま登録するということも考えられます。
idをユーザーが入力するのではなく、自動的にサーバーで採番すれば上記のことは避けられます。採番は「日付+その日のシーケンス番号」などとすることもできます。例えば、r_idをサロゲートキーとし、idを伝票番号などと自動的に採番する方法です。
いずれにしてもこの辺りはアプリケーションの仕様によって変わってきます。
★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/- 回答としてマーク 山本春海 2011年7月1日 2:45
すべての返信
-
ユーザーがidを入力するのであれば、実際にデータベースに保存するまで重複するかどうかはわかりません。よって、失敗する可能性は常に残るということです。失敗した時にどう扱うかはアプリケーションの仕様によります。何百と更新する場合、一つでも登録に失敗すれば全ての登録を取り消すか、登録できたものはそのまま登録するということも考えられます。
idをユーザーが入力するのではなく、自動的にサーバーで採番すれば上記のことは避けられます。採番は「日付+その日のシーケンス番号」などとすることもできます。例えば、r_idをサロゲートキーとし、idを伝票番号などと自動的に採番する方法です。
いずれにしてもこの辺りはアプリケーションの仕様によって変わってきます。
★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/- 回答としてマーク 山本春海 2011年7月1日 2:45
-
trapemiyaさま、いつもお世話になります。
マラソンの記録をとるDBアプリを作成していましたが、いろいろと問題が発生していて
今回の質問もその一つです。
r_idは、ご指摘の通りサロゲートキーとしているもので
idには個々の選手のゼッケン番号(n_id毎、s_id毎に一意)の数値を入力していきます。
r_id | id | results | n_id | s_id | time_stamp
1 | 131 | 04:38:51 | 10 | 4 | 2010/06/05 10:38:51....
2 | 27 | 04:38:53 | 10 | 4 | 2010/06/05 10:38:53....
~ 省略 ~
926 | 306 | 07:05:01 | 10 | 9 | 2010/06/05 13:05:01....
927 | 27 | 07:05:59 | 10 | 9 | 2010/06/05 13:05:59....
~ 省略 ~
3876 |157 | 05:21:00 | 11 | 4 | 2011/06/05 11:21:00....
3877 | 11 | 05:21:21 | 11 | 4 | 2011/06/05 11:21:21....
id列には、あらかじめ選手の登録数だけはわかるので、数値の範囲はわかるものの
どの選手(id)が何番目に入ってくるかわからないのです。
出走時点でキャンセルやリタイヤ等も考えられるのでidは必ずしも連番ではない(歯抜けあり)うえ
同一n_id(グループ中)でさらに同一s_id(グループの中)ではidは一意でなくてはならないので
このid列でIDENTITYのような仕組みを使えれば良いのでしょうが、それもできないので
今回、事前に重複idではないかをチェックする仕組みを実装できるのなら
「一つでも登録に失敗すれば全ての登録を取り消す」ことが無いようにできるのかな?と考えたのですが...
-
マラソンの続きだったのですね。
今回、事前に重複idではないかをチェックする仕組みを実装できるのなら
「一つでも登録に失敗すれば全ての登録を取り消す」ことが無いようにできるのかな?と考えたのですが...
おそらくTableAdapterもしくはSqlDataAdapterを利用されていると思います。DataGridViewの各行の更新は、各行ごとにSQLが発行されて処理されます。つまり、各行が独立して更新されるわけです。よって、トランザクションを切らなければ、更新に失敗した行以外はデータベースに保存されます。
さて、DataGridViewの全ての行を一括で更新する場合、RowUpdatingイベント、もしくはRowUpdatedイベントが役に立ちます。まずは以下を読んでみてください。
DataAdapter のイベント処理 (ADO.NET)
http://msdn.microsoft.com/ja-jp/library/6d1wk41s(v=VS.100).aspx#TableAdapterをご使用の場合は、そのAdapterプロパティでSqlDataAdapterを取得することができます。
なお、DataGridViewの全ての行を一括で更新する前に、DataGridView上で同じidが重複して登録されていないかをチェックされると良いと思います。もし最初に入力した方のidが誤っていた場合、誤ったidがデータベースに保存されてしまうからです。
#「一つでも登録に失敗すれば全ての登録を取り消す」という方針であれば、このようなチェックは必要ありません。個人的にはこちらの方がわかりやすいのではないかと思います。エラーが発生したとしてもDataGridViewには入力されたデータはそのまま残っていますし、エラーのあった行をエラーがあったことがわかるように表示されていますので、そこを修正してもう一度登録を試みるだけです。そのようにされる場合は、TransactionScopeを用いてトランザクションを切ってください。
★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/