トップ回答者
DataView での検索

質問
-
下記のように、少し長い説明で申し訳ありません。
DataView で特定のキー列を検索するときは、Sort メソッドで特定の列での並べ替えをしたうえででないと出来ないようですね。
これで、DataViewでの検索をするときは、Sortした列でFindメソッドを利用しますよね。
また、DataViewを複数列で並べ替えるときは、
(A):
DataView dv;
-----
dv.Sort = "CategoryCode ASC, ID DESC";
などとしますよね。(ID はインクリメント列とします)
で、このとき、複数の列での検索をするときには、
(B):
DataTable dt;
-----
object[] pk = new object[] { CategoryCode, ID };
int iRow = dv.Find(pk);
として、複数列の配列で検索出来ますよね。
また、この複数列での並べ替えのときに、一意のIDで検索したくても、DataViewの並べ替えが、
CategoryCode、ItemCode、ID の順というように、必要なキーとなる列が並べ替えの最後のキーであるときには、
上記の(B):同様の方法をとる方法がありますよね。
この最後の方法を取るとすると、必要なキーが一意のItemCodeだけでも、他の要らないCategoryCode、ItemCodeの列をも
object[] pk = new object[] { CategoryCode, ItemCode, ID };
int iRow = dv.Find(pk);
というように、検索のときに必要になってしまうのですが、
検索には余計なキーが2つ必要になるのが、初心者の感覚として無駄に感じてしまいます。
もちろん、最初からDataView を
dv.Sort = ID;
とすればよいのでしょうが(今回、DataGridViewのデータを、DataSource = dv; としている関係でこうなってしまっています)、
もっと他に簡単な方法はないかをお伺いしたいのです。
なお、今回この様なことが気になった背景としては、
まず、Windows Form でのInsertの際にCategoryCodeとItemCodeとを取得して、
次に、データのInsertの前にIDの最高値を取得して(int iMax)、
その後、Insert処理が終わったのちに
そのiMaxの値以上のIDのある行番号をdvで検索して(int iNew)、
そのiNewを利用して
dataGridView.CurrentCell = dataGridView[0, iNew];
という処理で、インサートしたばかりの行をdataGridViewの中で選択させたいという処理が念頭にあります。
何か無駄に感じるこの処理を簡潔に出来ないかという質問です。
説明が悪くて分かりにくかったら教えてください。
方法は、いろいろあるのかもしれませんが、どうぞ、よろしくお願いします。- 編集済み yasheeki 2009年10月12日 14:36 不明回避
回答
-
という処理で、インサートしたばかりの行をdataGridViewの中で選択させたいという処理が念頭にあります。
やりたいことが追加した行をCurrentCellにさせたいということであるようなので。
Sortを気にせずにバインド経由で操作してみる。
public class Form1 : Form { DataView dv; DataGridView grid; public Form1() { this.Width = 400; this.Height = 600; Button btn = new Button(); btn.Click += new EventHandler(btn_Click); this.Controls.Add(btn); grid = new DataGridView(); grid.Top = btn.Top + btn.Height; grid.Width = this.ClientSize.Width; grid.Height = this.ClientSize.Height - grid.Top; grid.Anchor = AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top; this.Controls.Add(grid); DataTable dt = new DataTable(); DataColumn clm1 = new DataColumn("ID" , typeof(int)); clm1.AutoIncrement = true; clm1.AutoIncrementSeed = 1; clm1.AutoIncrementStep = 1; DataColumn clm2 = new DataColumn("CategoryCode" , typeof(int)); DataColumn clm3 = new DataColumn("ItemCode" , typeof(int)); dt.Columns.AddRange(new DataColumn[] { clm1 , clm2 , clm3 }); Random rnd = new Random(); for (int i = 0; i < 10; i++) { dt.Rows.Add(null , rnd.Next(0,5) , rnd.Next(0,100)); } dv = new DataView(dt); dv.Sort="CategoryCode ASC, ID DESC"; grid.DataSource = dv ; } void btn_Click(object sender , EventArgs e) { //DataViewにバインドされているCurrencyManagerを取得する BindingManagerBase bmb = grid.BindingContext[dv]; CurrencyManager cm = (CurrencyManager)bmb; //新しい列を追加する DataRowView newRowView = ((DataView)cm.List).AddNew(); DataRow newRow = newRowView.Row; newRow[1] = 2; newRow[2] = 100; //BindingのPositionを追加した行に移動する cm.Position = cm.List.IndexOf(newRowView); //cm.Position = cm.List.Count - 1; } }
- 回答としてマーク 菊地俊介 2009年11月19日 9:06
-
-
DataTable の行の検索方法はざっくり以下のようなものが存在します。
- DataTable.Select
- SQL に似た構文で任意の列の値を検索します。
- DataRowCollection.Find
- 主キーとなる列の値を検索します。主キーとなっている列全てを検索列にしなければなりません。
- DataView.Find / FindRows
- ソート列の値を検索します。ソートしていなければ使えません。ソートしている列全てを検索列にしなければなりません。
- 拡張メソッドの Where
- コールバックを使って検索します。.NET 3.5 以降限定。利用可能な対象は、型付き DataTable、Cast<DataRow>() した DataRowCollection、同じく Cast<DataRowView>() した DataView などがあります。
- DataView や DataTable.Rows を foreach して自前で検索。
- そのままです。
DataView.RowFilter でフィルタ済みの行コレクションから検索するには、DataView.Find かあるいは Where 及び自前検索ぐらいしかありません。
もしフィルタと無関係に行を検索したいのなら、他の選択肢を選ぶことになります。
例えば主キーが ID 列一つの DataTable から特定の ID を持つ行を検索したいのなら、DataRowCollection.Find を使うのがベストでしょう。その場合フィルタは重要じゃないですし。しかし、主キーが複数の列で、そのうちの一つしか分からないのなら DataRowCollection.Find は使えません。Find は単一の行を取得するメソッドで、検索キーが「主キーのうち一つ」では行を一つに決定できないからです。
// というか、ID だけで特定できるのなら、何故他に主キー列が存在してるんでしょうかね?
DataTable.Select は、式の記述が多少面倒かも知れませんが、ID の検索程度なら一番良いと思います。
Where 及び自前検索は、最もフレキシブルに記述できますが、その分低速です。とはいえ相当大きなテーブルを扱わない限りは問題にはならないかも知れません。
それはそれとして、gekka さんが記述されたコードで本来の目的は達成できそうですが。
- 回答としてマーク 菊地俊介 2009年11月19日 9:06
-
インクリメント列のIDは主キーです。
このような記述をした場合、主キーはID列だけから構成されていると思うのが普通です。そこで、主キーの構成をお聞きしたのです。
CategoryCodeも ItemCodeも主キーであり、
IDの値を検索するという方法がわかりません。
それとも、DataDiewの元になっていたDataTableの主キーのことを指しているのでしょうか?
繰り返しになりますが、ID列のみで主キーが構成されているのであれば、DataRowCollectionのFindメソッドにIDのみ指定すれば検索できますということが言いたかったのです。
Hongliangさんも以下のように述べられていますが、私もそこに引っかかったのです。そこで主キーの構成をお聞きしました。
>// というか、ID だけで特定できるのなら、何故他に主キー列が存在してるんでしょうかね?
★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://blogs.wankuma.com/trapemiya/- 回答としてマーク 菊地俊介 2009年11月19日 9:06
-
横から失礼します。
ID を主キーにしたDataTableを用意し、
IDを条件に取得したい時は
=> DataRow row = table.Rows.Find(ID);
CategoryCodeとItemCodeで取得したい時は、
=> DataRow[] rows = table.Select("CategoryCode = '" + CategoryCode + "' AND ItemCode = '" + ItemCode + "'");
if( rows.Length != 0 )
{
DataRow row = rows[0];
}
こんな感じで取れるかと思いますけど、的外れだったらすいません- 回答としてマーク 菊地俊介 2009年11月19日 9:06
-
DataGridViewのDataSourceをDataViewではなくて、DataTableに置き換えるべきなのでしょうか?
カレント行の制御ですので、むしろBindingSourceをDataSourceに指定し、以下のようにされた方が良いのではないかと思います。
MyBindingSource.Position = MyBindingSource.Find("ID", ID);
★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://blogs.wankuma.com/trapemiya/- 回答としてマーク 菊地俊介 2009年11月19日 9:06
すべての返信
-
という処理で、インサートしたばかりの行をdataGridViewの中で選択させたいという処理が念頭にあります。
やりたいことが追加した行をCurrentCellにさせたいということであるようなので。
Sortを気にせずにバインド経由で操作してみる。
public class Form1 : Form { DataView dv; DataGridView grid; public Form1() { this.Width = 400; this.Height = 600; Button btn = new Button(); btn.Click += new EventHandler(btn_Click); this.Controls.Add(btn); grid = new DataGridView(); grid.Top = btn.Top + btn.Height; grid.Width = this.ClientSize.Width; grid.Height = this.ClientSize.Height - grid.Top; grid.Anchor = AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top; this.Controls.Add(grid); DataTable dt = new DataTable(); DataColumn clm1 = new DataColumn("ID" , typeof(int)); clm1.AutoIncrement = true; clm1.AutoIncrementSeed = 1; clm1.AutoIncrementStep = 1; DataColumn clm2 = new DataColumn("CategoryCode" , typeof(int)); DataColumn clm3 = new DataColumn("ItemCode" , typeof(int)); dt.Columns.AddRange(new DataColumn[] { clm1 , clm2 , clm3 }); Random rnd = new Random(); for (int i = 0; i < 10; i++) { dt.Rows.Add(null , rnd.Next(0,5) , rnd.Next(0,100)); } dv = new DataView(dt); dv.Sort="CategoryCode ASC, ID DESC"; grid.DataSource = dv ; } void btn_Click(object sender , EventArgs e) { //DataViewにバインドされているCurrencyManagerを取得する BindingManagerBase bmb = grid.BindingContext[dv]; CurrencyManager cm = (CurrencyManager)bmb; //新しい列を追加する DataRowView newRowView = ((DataView)cm.List).AddNew(); DataRow newRow = newRowView.Row; newRow[1] = 2; newRow[2] = 100; //BindingのPositionを追加した行に移動する cm.Position = cm.List.IndexOf(newRowView); //cm.Position = cm.List.Count - 1; } }
- 回答としてマーク 菊地俊介 2009年11月19日 9:06
-
-
インクリメント列のIDは主キーです。
このときのDataRowCollectionの使用方法は、どうなるのでしょうか?
Visual Studioのヘルプには、
DataRow foundRow = table.Rows.Find(pkValue);
という使用方法が紹介されていますが、
今回、
CategoryCodeも ItemCodeも主キーであり、
IDの値を検索するという方法がわかりません。
それとも、DataDiewの元になっていたDataTableの主キーのことを指しているのでしょうか?
DataTable table;
-----
int pkValue;
pkValue = XXXXX;
DataRow foundRow = table.Rows.Find(pkValue);
XXXXX がわかりません。
よろしくお願いします。 -
DataTable の行の検索方法はざっくり以下のようなものが存在します。
- DataTable.Select
- SQL に似た構文で任意の列の値を検索します。
- DataRowCollection.Find
- 主キーとなる列の値を検索します。主キーとなっている列全てを検索列にしなければなりません。
- DataView.Find / FindRows
- ソート列の値を検索します。ソートしていなければ使えません。ソートしている列全てを検索列にしなければなりません。
- 拡張メソッドの Where
- コールバックを使って検索します。.NET 3.5 以降限定。利用可能な対象は、型付き DataTable、Cast<DataRow>() した DataRowCollection、同じく Cast<DataRowView>() した DataView などがあります。
- DataView や DataTable.Rows を foreach して自前で検索。
- そのままです。
DataView.RowFilter でフィルタ済みの行コレクションから検索するには、DataView.Find かあるいは Where 及び自前検索ぐらいしかありません。
もしフィルタと無関係に行を検索したいのなら、他の選択肢を選ぶことになります。
例えば主キーが ID 列一つの DataTable から特定の ID を持つ行を検索したいのなら、DataRowCollection.Find を使うのがベストでしょう。その場合フィルタは重要じゃないですし。しかし、主キーが複数の列で、そのうちの一つしか分からないのなら DataRowCollection.Find は使えません。Find は単一の行を取得するメソッドで、検索キーが「主キーのうち一つ」では行を一つに決定できないからです。
// というか、ID だけで特定できるのなら、何故他に主キー列が存在してるんでしょうかね?
DataTable.Select は、式の記述が多少面倒かも知れませんが、ID の検索程度なら一番良いと思います。
Where 及び自前検索は、最もフレキシブルに記述できますが、その分低速です。とはいえ相当大きなテーブルを扱わない限りは問題にはならないかも知れません。
それはそれとして、gekka さんが記述されたコードで本来の目的は達成できそうですが。
- 回答としてマーク 菊地俊介 2009年11月19日 9:06
-
インクリメント列のIDは主キーです。
このような記述をした場合、主キーはID列だけから構成されていると思うのが普通です。そこで、主キーの構成をお聞きしたのです。
CategoryCodeも ItemCodeも主キーであり、
IDの値を検索するという方法がわかりません。
それとも、DataDiewの元になっていたDataTableの主キーのことを指しているのでしょうか?
繰り返しになりますが、ID列のみで主キーが構成されているのであれば、DataRowCollectionのFindメソッドにIDのみ指定すれば検索できますということが言いたかったのです。
Hongliangさんも以下のように述べられていますが、私もそこに引っかかったのです。そこで主キーの構成をお聞きしました。
>// というか、ID だけで特定できるのなら、何故他に主キー列が存在してるんでしょうかね?
★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://blogs.wankuma.com/trapemiya/- 回答としてマーク 菊地俊介 2009年11月19日 9:06
-
説明が不足してまして申し訳ありません。
ID列のみが主キーで、CategoryCodeとItemCodeは外部キーです。
この説明がまずくて誤解を招いてしまったようです。
CategoryCode、ItemCode、ID の順と書きましたが、
このとき、CategoryCode, ItemCode は外部キーでしかありません。
主キーはあくまでも ID列です。
DataTableから、DataRowCollection.Find を使うのがベストということでしょうか?
こうしたことは、DataViewでは、
ソート列の値を検索します。ソートしていなければ使えません。ソートしている列全てを検索列にしなければなりません。
ということですね?
今みなさんの返信を確認出来たばかりで、まだ単純にDavaView での検索方法を確認しているところです。
まだ、ご回答いただいたものをのみ込めておりませんが、理解するまであと何日かかかると思いますので、いったんお礼を申し上げます。 -
横から失礼します。
ID を主キーにしたDataTableを用意し、
IDを条件に取得したい時は
=> DataRow row = table.Rows.Find(ID);
CategoryCodeとItemCodeで取得したい時は、
=> DataRow[] rows = table.Select("CategoryCode = '" + CategoryCode + "' AND ItemCode = '" + ItemCode + "'");
if( rows.Length != 0 )
{
DataRow row = rows[0];
}
こんな感じで取れるかと思いますけど、的外れだったらすいません- 回答としてマーク 菊地俊介 2009年11月19日 9:06
-
DataGridViewのDataSourceをDataViewではなくて、DataTableに置き換えるべきなのでしょうか?
カレント行の制御ですので、むしろBindingSourceをDataSourceに指定し、以下のようにされた方が良いのではないかと思います。
MyBindingSource.Position = MyBindingSource.Find("ID", ID);
★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://blogs.wankuma.com/trapemiya/- 回答としてマーク 菊地俊介 2009年11月19日 9:06