none
DataView での検索 RRS feed

  • 質問

  • 下記のように、少し長い説明で申し訳ありません。

    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 不明回避
    2009年10月12日 14:26

回答

  • という処理で、インサートしたばかりの行を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
    2009年10月12日 16:48
  • IDがインクリメント列であれば、それだけで主キーになりえます。今、主キーの設定はどのようにされているのでしょうか? もし、主キーがIDであれば、DataViewのFindメソッドではなくDataRowCollectionのFindメソッドが使えます。
    また、LINQが使える環境でしたらLINQで行えばスマートです。


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://blogs.wankuma.com/trapemiya/
    • 回答としてマーク 菊地俊介 2009年11月19日 9:06
    2009年10月13日 0:54
    モデレータ
  • 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
    2009年10月13日 14:27
  • インクリメント列の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
    2009年10月13日 14:53
    モデレータ
  • 横から失礼します。

    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
    2009年10月23日 8:36
  • 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
    2009年10月23日 15:58
    モデレータ

すべての返信

  • という処理で、インサートしたばかりの行を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
    2009年10月12日 16:48
  • IDがインクリメント列であれば、それだけで主キーになりえます。今、主キーの設定はどのようにされているのでしょうか? もし、主キーがIDであれば、DataViewのFindメソッドではなくDataRowCollectionのFindメソッドが使えます。
    また、LINQが使える環境でしたらLINQで行えばスマートです。


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://blogs.wankuma.com/trapemiya/
    • 回答としてマーク 菊地俊介 2009年11月19日 9:06
    2009年10月13日 0:54
    モデレータ
  • インクリメント列の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 がわかりません。
    よろしくお願いします。

    2009年10月13日 12:18
  • 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
    2009年10月13日 14:27
  • インクリメント列の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
    2009年10月13日 14:53
    モデレータ
  • 説明が不足してまして申し訳ありません。

    ID列のみが主キーで、CategoryCodeとItemCodeは外部キーです。
    この説明がまずくて誤解を招いてしまったようです。

    CategoryCode、ItemCode、ID の順と書きましたが、
    このとき、CategoryCode, ItemCode は外部キーでしかありません。
    主キーはあくまでも ID列です。

    DataTableから、DataRowCollection.Find を使うのがベストということでしょうか?
    こうしたことは、DataViewでは、
    ソート列の値を検索します。ソートしていなければ使えません。ソートしている列全てを検索列にしなければなりません。
    ということですね?

    今みなさんの返信を確認出来たばかりで、まだ単純にDavaView での検索方法を確認しているところです。
    まだ、ご回答いただいたものをのみ込めておりませんが、理解するまであと何日かかかると思いますので、いったんお礼を申し上げます。
    2009年10月17日 12:35
  • 横から失礼します。

    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
    2009年10月23日 8:36
  • DataGridView のDataSource がDataView のときに、このDataGridViewで該当データを選択するときにDataTableのデータの該当行を検索しても、DataGridViewの該当データが選択できないので、DataGridViewのDataSourceになっているDataViewでの行番号を検索したいのです。

    DataGridViewのDataSourceをDataViewではなくて、DataTableに置き換えるべきなのでしょうか?
    もしも、見当違いの質問であったら、すみません。
    2009年10月23日 11:38
  • 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
    2009年10月23日 15:58
    モデレータ
  • ありがとうございます。

    BindingSourceをDataSourceにすることを確かめている最中ですが、
    手間取っておりますので、解決あるいは、新たな結果を得たならばすぐにでも返信したいと思います。

    時間がかかりすぎで申し訳ありません。

    2009年11月3日 11:09
  • こんにちは。

    回答者の皆様、詳しい回答ありがとうございます。

    yasheekiさん、フォーラムのご利用ありがとうございます。
    その後いかがでしょうか。何か進展はありましたか?
    追加の質問等ありましたら、ぜひ投稿してみてください。

    勝手ながら、有用な情報と思われる回答へ回答マークをつけさせていただきました。

    今後ともフォーラムをよろしくお願いします。
    それでは!

    2009年11月19日 9:12