(.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 + "'");
文字列検索するときは引用符の付け忘れに注意しましょう。
引用符つけなくても一応動いてしまうので厄介です。
参考: