none
DataGridでのオートナンバーを制御したい RRS feed

  • 質問

  • いつもお世話になります。 danchorと申します。


    VB.NET2003+Access2000環境です。
    データグリッドでいくつか質問させていただいておりますが、これも関連質問です。


    今運用しているアプリで、担当者から指摘され改善要望をうけている現象ですが、具体的にこの画面の中には
     (1)メインのテーブル(テキストボックスとコンボボックスで構成)
     (2)明細1のデータグリッド
     (3)明細2のデータグリッド
    があります。


    明細1は(1)の伝票Noをキーにして、オートナンバーで明細1のNoを発生させています。(主キーに設定しています)


    明細2は同じく(1)の伝票をキーに(2)で発生した明細Noを明示的に手入力し、その明細2Noもオートナンバーで発行しています。(これもオートナンバーで発行したデータを主キーにしています)


    明細2に入力する明細1で発生したナンバーは必ずしも全ての明細2の列に入るのではなく、nullの場合もあります。(明細2の行数の方が明細1の行数より常に多く、明細1のデータは、明細2のある1行にだけしか利用しない)

     


    こういう状況の中、明細1のデータグリッドにデータを入力するとき、ある列にカーソルを持って行くとオートナンバーで明細Noが現れます。


    入力をして1行下げると新しい明細Noが現れますが、何も入力しないで上の行にフォーカスを移すと新規発生した明細Noはいったん消えます。


    そのまま再度下行にフォーカスを移すと先ほど一旦現れたNoの次の数字(+1された数字)が現れます。


    これを繰り返すとどんどんこの数字は大きくなって行きます。

    しかし見かけ上飛んでいるナンバーの行に入力して保存し、再度呼び出したら、空き番は自動的に詰められて連番に変わってしまっています。


    これでは明細2に明示的に入力したデータと不整合になってしまいます。


    長々とかきましたが、要はオートナンバーが、実際には使用されていないのにどんどん見かけ上だけインクリメントするのを防ぐにはどうしたらいいのでしょうかと言うことです。

     

    あるいは本来使われるべき数字をきちんと表示させるようにするにはどうしたらいいのか?  模索しています。

    具体的な方法をご存じの方、ご教示ください。


    また小生の考え方(使い方)が誤っているというご指摘でも結構ですのでアドバイスをお願いします。

     

     

    2007年4月18日 23:59

回答

  •  danchor さんからの引用

         「System.Data.InvalidConstraintException' のハンドルされていない例外が system.data.dll で発生しました。

    追加情報 : 親列および子列に型が一致する列が含まれていません。」

     

    というエラーが起こります。

     

    当初オートナンバー型と数値型での不整合かと思い、ブレークしながらチェックしていてそうではないことがわかりました。

     

    もともとあった親テーブルとデータグリッド1およびデータグリッド2のリレーションがはずれてしまったためのエラーだとわかりました。


    リレーションはデータテーブル同士の間に存在するもので、データテーブルとデータグリッドとの間のリレーションというのが、ちょっとよくわかりません。

     

     danchor さんからの引用

    これにテーブルBとCの商品NOでリレーションをかけなければ、本来質問しているオートナンバーの数字が変わってしまう問題が解消されないということになりますよね。


    その通りです。

     

     danchor さんからの引用

    テーブルAとの関連を損なわずにそれぞれが連動するようにするにはどう設計(設定)すればいいのでしょうか?

    勘違いしているかもしれませんが、以下のようなことですよね?

     

    DataRelation rel = new DataRelation("relation12", dataSet1.Tables["table1"].Columns["ID"], dataSet1.Tables["table2"].Columns["ID"], true);
    dataSet1.Relations.Add(rel);
    rel = new DataRelation("relation13", dataSet1.Tables["table1"].Columns["ID"], dataSet1.Tables["table3"].Columns["ID"], true);
    dataSet1.Relations.Add(rel);
    rel = new DataRelation("relation23", dataSet1.Tables["table2"].Columns["TABLE2ID"], dataSet1.Tables["table3"].Columns["ID2"], true);
    dataSet1.Relations.Add(rel);

     

     danchor さんからの引用

    もう一点、それまでの当面の回避策として、オートナンバーの値を予測して予め修正してやることを考えています。


    オートナンバーは、その名の通り、自動的に番号を振ってくれるものです。もし、その値を予測したものを使いたいのであれば、最初からオートナンバーは使わず、自分で採番するロジックを組み込めば良いでしょう。マルチユーザー環境でなければ、知らない間に誰かが新しい番号を取得するという心配がないので、比較的容易だと思います。

    オートナンバーはその概念上、次はどの番号なのかを知る方法はないんじゃないかと思います。(すみません、断言はできません)

    2007年4月27日 5:45
    モデレータ

すべての返信

  •  danchor さんからの引用

    長々とかきましたが、要はオートナンバーが、実際には使用されていないのにどんどん見かけ上だけインクリメントするのを防ぐにはどうしたらいいのでしょうかと言うことです。

    これはちょっと難しいんじゃないかと思います。

     

     danchor さんからの引用

    あるいは本来使われるべき数字をきちんと表示させるようにするにはどうしたらいいのか?  模索しています。

    システムの仕様としていいか悪いかは別として、明細1と明細2の間にDataRelationを設定しておけば、明細1を保存した段階で、明細2のオートナンバーは明細1のそれで自動的に更新されるはずです。
    2007年4月19日 5:07
    モデレータ
  •  trapemiya さん ありがとうございます。

     

     

     trapemiya さんからの引用

     danchor さんからの引用

    長々とかきましたが、要はオートナンバーが、実際には使用されていないのにどんどん見かけ上だけインクリメントするのを防ぐにはどうしたらいいのでしょうかと言うことです。

    これはちょっと難しいんじゃないかと思います。

     

     danchor さんからの引用

    あるいは本来使われるべき数字をきちんと表示させるようにするにはどうしたらいいのか?  模索しています。

    システムの仕様としていいか悪いかは別として、明細1と明細2の間にDataRelationを設定しておけば、明細1を保存した段階で、明細2のオートナンバーは明細1のそれで自動的に更新されるはずです。

     

    説明がちょっとまずくてよく伝わらなかったようですので・・・。

     

    例として 明細1で発生したオートナンバーのデータが「100」番目だったとします。

    これを見て明細2のフィールドに「100」と手入力します。

    でも実際には明細1のデータは「99」番であったりするわけで、手入力したデータまで自動的に「99」に修正されたりはしませんよね。

    これで困っているわけです。

    明細2のある行のデータの詳細のデータになるわけで、ひも付けを確実にやっておかねばならないのです。

     

    なにかいい手はないものでしょうか?

     

     

    2007年4月19日 6:56
  •  danchor さんからの引用

    説明がちょっとまずくてよく伝わらなかったようですので・・・。

    今のところ、私は質問内容を間違って受け取っていないつもりですが、もし、まだ私が勘違いしているようであれば、ご指摘下さい。

     

     danchor さんからの引用
      

    例として 明細1で発生したオートナンバーのデータが「100」番目だったとします。

    これを見て明細2のフィールドに「100」と手入力します。

    でも実際には明細1のデータは「99」番であったりするわけで、手入力したデータまで自動的に「99」に修正されたりはしませんよね。

    いえ、自動的に修正されます。これがDataRelationの効力です。もし、DataRelationで明細1のあるカラムと明細2のあるカラムが結ばれていた場合、明細1のあるカラムの値を修正すると、自動的に明細2の対応するカラムの値も同じ値になります。

    さて、この前提知識を元に、次に行きます。

    データテーブル上のオートナンバーはあくまで仮の番号です。正しい番号は、mdbに保存されて初めて決定されます。この番号をデータテーブルに読み直しされていると思います。ここまではdanchorさんはされていると思います。ここでもしDataRelationが設定してあればどうでしょう? 明細2の対応するカラムも自動的に変更されることになります。

    2007年4月20日 1:36
    モデレータ
  •  trapemiya さんからの引用
     

    今のところ、私は質問内容を間違って受け取っていないつもりですが、

     

     danchor さんからの引用
      

    例として 明細1で発生したオートナンバーのデータが「100」番目だったとします。

    これを見て明細2のフィールドに「100」と手入力します。

    でも実際には明細1のデータは「99」番であったりするわけで、手入力したデータまで自動的に「99」に修正されたりはしませんよね。

    いえ、自動的に修正されます。これがDataRelationの効力です。もし、DataRelationで明細1のあるカラムと明細2のあるカラムが結ばれていた場合、明細1のあるカラムの値を修正すると、自動的に明細2の対応するカラムの値も同じ値になります。

    さて、この前提知識を元に、次に行きます。

    データテーブル上のオートナンバーはあくまで仮の番号です。正しい番号は、mdbに保存されて初めて決定されます。この番号をデータテーブルに読み直しされていると思います。ここまではdanchorさんはされていると思います。ここでもしDataRelationが設定してあればどうでしょう? 明細2の対応するカラムも自動的に変更されることになります。

     

    私がすっかり誤解した固定観念を持っていたのが原因のようです。

    「そんなことはできっこない」と決めてかかっておりました。

    誠に失礼いたしました。

     

    DataGridViewでDataRelationですか、全く使ったことがないのでイメージがわかないのですが、

    メインテーブルとサブテーブルを関連づけているリレーションと同じ考え方なんでしょうね。

     

    あるカラム同士のリレーションって具体的にはどう勉強すればいいのでしょうか?

    よろしくご教示お願いいたします。

     

     

     

    2007年4月20日 2:07
  • C#で以前書いたテストコードです。

     

    Convert C# to VB.NET
     http://www.developerfusion.co.uk/utilities/convertcsharptovb.aspx

     

    のサイトあたりでVB.NETへ変換して下さい。

     

    Code Snippet

    oleDbDataAdapter1.MissingSchemaAction = MissingSchemaAction.AddWithKey;
    oleDbDataAdapter1.FillSchema(dataSet1, SchemaType.Source, "table1");

     

    //SeedとStepは明示的に指定する必要あり。
    dataSet1.Tables["table1"].Columns["ID"].AutoIncrementSeed = -1;
    dataSet1.Tables["table1"].Columns["ID"].AutoIncrementStep = -1;

     

    oleDbDataAdapter1.Fill(dataSet1, "table1");
    dataGrid1.SetDataBinding(dataSet1, "table1");

     

    oleDbDataAdapter2.MissingSchemaAction = MissingSchemaAction.AddWithKey;
    oleDbDataAdapter2.FillSchema(dataSet1, SchemaType.Source, "table2");

     

    //SeedとStepは明示的に指定する必要あり。
    dataSet1.Tables["table2"].Columns["TABLE2ID"].AutoIncrementSeed = -1;
    dataSet1.Tables["table2"].Columns["TABLE2ID"].AutoIncrementStep = -1;

     

    oleDbDataAdapter2.Fill(dataSet1, "table2");
    dataGrid2.SetDataBinding(dataSet1, "table2");

     

    //table1のIDとtable2のIDの間にリレーションを設定。デフォルトで外部キー制約が付く。nullの場合は、外部キー制約を受けない。
    DataRelation rel = new DataRelation("relation12", dataSet1.Tables["table1"].Columns["ID"], dataSet1.Tables["table2"].Columns["ID"], true);
    dataSet1.Relations.Add(rel);

     

     

    2007年4月20日 3:08
    モデレータ
  •  

    すみません 追加で教えてください。

     

    ご教示いただいたコードを参考にいろいろとやってみてはいるのですが、なかなかうまくいきません。

     

    まずほとんどそのままでVB用に変換して実行すると、

     

         「System.Data.InvalidConstraintException' のハンドルされていない例外が system.data.dll で発生しました。

    追加情報 : 親列および子列に型が一致する列が含まれていません。」

     

    というエラーが起こります。

     

    当初オートナンバー型と数値型での不整合かと思い、ブレークしながらチェックしていてそうではないことがわかりました。

     

    もともとあった親テーブルとデータグリッド1およびデータグリッド2のリレーションがはずれてしまったためのエラーだとわかりました。

     

    3つのテーブルにまたがる2組のリレーションはいったいどうすれば? と悩んでいます。

     

    テーブルA(親テーブル)      日報NO、・・・・・・・・・ 

    テーブルB(商品テーブル)    日報NO、商品NO(オートナンバー)、・・・・・・・

    テーブルC(明細テーブル)    日報NO、明細NO(オートナンバー)、商品NO(数値型、手入力)、・・・・

     

    という構成であり、現在は日報NOをキーにそれぞれにリレーションがかかっています。

     

    これにテーブルBとCの商品NOでリレーションをかけなければ、本来質問しているオートナンバーの数字が変わってしまう問題が解消されないということになりますよね。

     

    これがなかなかうまく行ってくれません。

    BとCに関連がつけばAとは切り離され最初のレコードから全てが表示される等々。

     

    テーブルAとの関連を損なわずにそれぞれが連動するようにするにはどう設計(設定)すればいいのでしょうか?

    ご教示ください。

     

     

    もう一点、それまでの当面の回避策として、オートナンバーの値を予測して予め修正してやることを考えています。

    フォーム上にでている数字は保存される事によって正しく置き換えられ、それを再読込させることによって手入力した数字を修正することは可能ですが、仮のオートナンバーはメモリ上にあり、一旦終了されるまでずっと狂いつづけてどんどん数字は増え続けます。

     

    一旦保存されて正しくデータが修正された時点で、いまメモリ上に持っている仮のオートナンバー値を知ることができれば打つ手は出来ます。

     

    どうしたら次に現れるオートナンバー値あるいは現在の値を知ることができるのでしょうか?

     

    併せてよろしくお願いいたします。

     

     

    2007年4月27日 4:32
  •  danchor さんからの引用

         「System.Data.InvalidConstraintException' のハンドルされていない例外が system.data.dll で発生しました。

    追加情報 : 親列および子列に型が一致する列が含まれていません。」

     

    というエラーが起こります。

     

    当初オートナンバー型と数値型での不整合かと思い、ブレークしながらチェックしていてそうではないことがわかりました。

     

    もともとあった親テーブルとデータグリッド1およびデータグリッド2のリレーションがはずれてしまったためのエラーだとわかりました。


    リレーションはデータテーブル同士の間に存在するもので、データテーブルとデータグリッドとの間のリレーションというのが、ちょっとよくわかりません。

     

     danchor さんからの引用

    これにテーブルBとCの商品NOでリレーションをかけなければ、本来質問しているオートナンバーの数字が変わってしまう問題が解消されないということになりますよね。


    その通りです。

     

     danchor さんからの引用

    テーブルAとの関連を損なわずにそれぞれが連動するようにするにはどう設計(設定)すればいいのでしょうか?

    勘違いしているかもしれませんが、以下のようなことですよね?

     

    DataRelation rel = new DataRelation("relation12", dataSet1.Tables["table1"].Columns["ID"], dataSet1.Tables["table2"].Columns["ID"], true);
    dataSet1.Relations.Add(rel);
    rel = new DataRelation("relation13", dataSet1.Tables["table1"].Columns["ID"], dataSet1.Tables["table3"].Columns["ID"], true);
    dataSet1.Relations.Add(rel);
    rel = new DataRelation("relation23", dataSet1.Tables["table2"].Columns["TABLE2ID"], dataSet1.Tables["table3"].Columns["ID2"], true);
    dataSet1.Relations.Add(rel);

     

     danchor さんからの引用

    もう一点、それまでの当面の回避策として、オートナンバーの値を予測して予め修正してやることを考えています。


    オートナンバーは、その名の通り、自動的に番号を振ってくれるものです。もし、その値を予測したものを使いたいのであれば、最初からオートナンバーは使わず、自分で採番するロジックを組み込めば良いでしょう。マルチユーザー環境でなければ、知らない間に誰かが新しい番号を取得するという心配がないので、比較的容易だと思います。

    オートナンバーはその概念上、次はどの番号なのかを知る方法はないんじゃないかと思います。(すみません、断言はできません)

    2007年4月27日 5:45
    モデレータ
  •  trapemiya さんからの引用

     

    DataRelation rel = new DataRelation("relation12", dataSet1.Tables["table1"].Columns["ID"], dataSet1.Tables["table2"].Columns["ID"], true);
    dataSet1.Relations.Add(rel);
    rel = new DataRelation("relation13", dataSet1.Tables["table1"].Columns["ID"], dataSet1.Tables["table3"].Columns["ID"], true);
    dataSet1.Relations.Add(rel);
    rel = new DataRelation("relation23", dataSet1.Tables["table2"].Columns["TABLE2ID"], dataSet1.Tables["table3"].Columns["ID2"], true);
    dataSet1.Relations.Add(rel);

     

     danchor からの引用

    もう一点、それまでの当面の回避策として、オートナンバーの値を予測して予め修正してやることを考えています。


    オートナンバーは、その名の通り、自動的に番号を振ってくれるものです。もし、その値を予測したものを使いたいのであれば、最初からオートナンバーは使わず、自分で採番するロジックを組み込めば良いでしょう。マルチユーザー環境でなければ、知らない間に誰かが新しい番号を取得するという心配がないので、比較的容易だと思います。

    オートナンバーはその概念上、次はどの番号なのかを知る方法はないんじゃないかと思います。(すみません、断言はできません)

     

    リレーションはおっしゃるとおりです。

    これを参考にさせていただいて少しさわりましたが、基本的には動作するものの、思いがけないところでエラーが起こったりして、結果として目的には到達出来ていません。

     

    実はテーブルAは、そのプログラムの中で数値型で採番しています。

     

    横着して参考にしていた本のサンプルの考え方をそのまま使ったためにこんな現象に巻き込まれています。

    原因がはっきりとしているために、この際自分で採番する方式にあらためます。

     

    いろいろご教示いただきましたことを感謝申し上げます。

    今後ともよろしくご指導お願いいたします。

     

     

     

    2007年4月27日 7:45