(.Net)DataTable.Selectの条件は引用符でくくらないとおかしなことになる
DataTable.Selectメソッドを使って、データの抽出を行ってました。
下記のように検索対象の列は文字列型ですが、中身は数値が入っています。

string strNo = "10";
 
DataTable tbl = new DataTable();
tbl.Columns.Add("no");
tbl.Columns.Add("name");
 
tbl.Rows.Add(new object[] { "1", "hoge" });
tbl.Rows.Add(new object[] { "10", "hoge" });
tbl.Rows.Add(new object[] { "2", "hoge" });
tbl.Rows.Add(new object[] { "20", "hoge" });
 
DataRow[] rows = tbl.Select("no=" + strNo);


検索値を 10 とすると以下のような例外が select メッソド実行時に発生します。

System.ArgumentException がキャッチされました
Message="Range オブジェクトの Min (1) は、 max (-1) 以下でなければなりません。"
Source="System.Data"
StackTrace:
場所 System.Data.Select.GetBinaryFilteredRecords()
場所 System.Data.Select.SelectRows()
場所 System.Data.DataTable.Select(String filterExpression)
場所 Hoge.hoge() 場所 D:\mydoc\devlop\hoge.cs:行 12
InnerException:


ちなみに、検索値を他の値に設定するとこのエラーが出ません。

で、原因を調べた結果、MSDNフォーラム: VB2005 .NET2.0 DataTable.Select()メソッドに関してに答えが。。

ほぼコピペですが、詳しい話を。。
DataTable.Selectメソッドの内部ではバイナリサーチ(二分探索)を使っているようです。
バイナリサーチは、一旦リストをソートしてから出ないと実行できません。
検索列は文字列型なので、ソートすると下記のようになります。(文字列上の昇順になるわけです)

1,10,2,20

バイナリサーチはソートしたリストから中央の値を検索値と比較して、検索したい値が中央の値の右にあるか、左にあるかを判断するので、この場合、2番目以降(値10)に値があると判断されます。
そして、10の値がどこまで続いているかを調べるためにバイナリサーチではもう一回検索されるわけですが、その時検索値の 10 とリスト内の 2 が比較された結果、10 > 2となり、DataTable内の "2" と "20" の間に 10 があるはずだとなって、エラーになるようですね。

対策として、下記のようにSelectメソッドの検索値に引用符(シングルクォーテーション)を付けて明示的に文字列だとすればこのエラーは回避できます。

DataRow[] rows = tbl.Select("no='" + strNo + "'");


文字列検索するときは引用符の付け忘れに注意しましょう。
引用符つけなくても一応動いてしまうので厄介です。

参考:
DataTable.Select の条件式での型推論による弊害
[PR]
by Jehoshaphat | 2012-05-15 23:39 | .Net開発 | Trackback | Comments(1)
トラックバックURL : http://jehupc.exblog.jp/tb/17992070
トラックバックする(会員専用) [ヘルプ]
※このブログはトラックバック承認制を適用しています。 ブログの持ち主が承認するまでトラックバックは表示されません。
Commented by 通りすがり at 2013-06-20 12:51 x
おそらくですが。
DataTableにカラムを追加する際、データ型に指定がなければ「文字列型」となったとおもいます。
文字列型のデータを整数型などで検索しようとしたため例外となったのでは?

tbl.Select("no=" + strNo); // ←これ

下のコードは、データ型まで指定したコードです。

--

string strNo = "10";

DataTable tbl = new DataTable();

DataColumn clm = new DataColumn();
clm.ColumnName = "no";
clm.DataType = typeof(Int32);
tbl.Columns.Add(clm);

tbl.Columns.Add("name");

tbl.Rows.Add(new object[] { 1, "hoge" });
tbl.Rows.Add(new object[] { 10, "hoge" });
tbl.Rows.Add(new object[] { 2, "hoge" });
tbl.Rows.Add(new object[] { 20, "hoge" });

DataRow[] rows = tbl.Select("no=" + strNo);

Console.WriteLine(rows.Length);


<< ドライバサービスを起動しないよ... DFSサーバ障害時にすべきこと >>