トップ回答者
GridViewで複数条件あいまい検索

質問
-
visual web developerでWEBアプリを作っています。
GridView(SQLDatasource)にデータの一覧を表示させ,
ajax cotrol toolkitでコンボボックスをいくつか配置し,
コンボボックスに入力された内容によりGridViewに絞り込みして表示させたいです。
データ一覧は以下のような感じです。
氏名 性別 県名
AB 男 東京
BC 女 神奈川
CD 女
DE 男
EF 男 東京
FG 神奈川
性別が「男」の場合は,
AB,DE,EFが,
県名が「神奈」の場合は,
BC,FGが,
性別が「女」で県名が「神奈川」の場合は
BC
が抽出されるようにしたいです。
氏名,性別,県名ともに空欄もありえ,あいまい一致で抽出しています。
例では列数を3つにしましたが,実際には10列くらいあって,抽出したい条件列は5つくらいです。
いまは,SQLDatasourceに以下のように書いているので,
両方にデータが入っているものしか抽出されません。
<asp:SqlDataSource ID="SqlDataSource3" runat="server"
ConnectionString="<%$ ConnectionStrings:BKANMTConnectionString3 %>"
SelectCommand="SELECT ID, 氏名, 性別, 県名 FROM MAIN WHERE (氏名 LIKE '%' + @氏名 + '%') AND WHERE (性別 LIKE '%' + @性別 + '%') AND WHERE (県名 LIKE '%' + @県名 + '%')">
<SelectParameters>
<asp:ControlParameter ControlID="cmb1" Name="氏名" PropertyName="Text"
Type="String" ConvertEmptyStringToNull="False" />
<asp:ControlParameter ControlID="cmb2" ConvertEmptyStringToNull="False"
Name="性別" PropertyName="SelectedValue" />
<asp:ControlParameter ControlID="cmb3" ConvertEmptyStringToNull="False"
Name="県名" PropertyName="SelectedValue" />
</SelectParameters>
</asp:SqlDataSource>
以下のサイトを参考にしてみたのですが,
http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=41727&forum=7
パラメータ?が登場してきているあたりで分からなくなってしまいました。
SQLDatasourceのSelectCommand="・・・の抽出条件のところの文を書き直すだけで完成するのでしょうか?
ご教授よろしくお願いいたします。- 移動 星 睦美 2014年4月24日 7:34 Visual Studio Express Edition - 全般 から
回答
-
とは限らず,条件に使う列が5つくらいあって,
そのうち1つだけで絞り込む時もあれば5つ全部で絞り込む時もあります・・・。
やっぱり条件文だけでは難しいのですね。
ということで
ストアドプロシージャの基礎をネットで検索して勉強し始めています!
いえ、私が示したSQL文が、「そのうち1つだけで絞り込む時もあれば5つ全部で絞り込む時もあります・・・。」を解決する内容になっています。これ以上のことをやろうとするとストアドプロシージャでwhere部分を組み立てることになります。また、厳密に言えばlikeに指定する検索ワードはエスケープしなければなりません。なぜなら、検索ワードに例えば[を指定した場合、これはlikeにおける検索では特殊な意味を持ってしまい、[を検索ワードと見做してくれないからです。★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
- 回答としてマーク masunona 2014年4月24日 6:09
-
ソースを元に戻されたのかもしれませんが、is not nullの追加や、カッコの数が合っていないなど中途半端です。正しくは以下のようになります。見やすいように整形しています。
where ( 担当 like (case when @担当 <> '' and @担当 is not null then '%' + @担当 + '%' else '%' end) ) and ( 確度 like (case when @確度 <> '' and @確度 is not null then '%' + @確度 + '%' else '%' end) ) and ( ユーザ like (case when @ユーザ <> '' and @ユーザ is not null then '%' + @ユーザ + '%' else '%' end) ) and ( 症状 like (case when @症状 <> '' and @症状 is not null then '%' + @症状 + '%' else '%' end) )
これでも動かない場合は、実際にどのようなSQL文が投げられているかSQL Server Profilerで確認してみて下さい。
Windows 7ですと、スタートボタン -> すべてのプログラム -> Microsoft SQL Server -> パフォーマンスツール の中にあります。
★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
- 回答としてマーク masunona 2014年5月15日 0:59
-
SQL Server Profilerはインストールされていないようです。
「パフォーマンスツール」というフォルダ自体がありません。
?Express版なんですね。でしたら残念ながらありません。この辺りは重要な情報ですので、次からはここまで書くようにして下さいね。
さて、担当 like (case when @担当 <> '' and @担当 is not null
then '%' + @担当 + '%'
else '%'
end)この意味がわかりますでしょうか? @担当が空文字ではない、かつnullでもなければ、検索のキーワードで検索し、そうでなければ%のみを指定することによりすべてにマッチするようになりますから、実質、何も検索条件に指定していないのと同じになります。
流れで上のようなSQLになっていますが、以下のように書き換えても同様です。where ( 担当 like (case when @担当 = '' or @担当 is null then '%' else '%' + @担当 + '%' end) ) and ( 確度 like (case when @確度 = '' or @確度 is null then '%' else '%' + @確度 + '%' end) ) and ( ユーザ like (case when @ユーザ = '' or @ユーザ is null then '%' else '%' + @ユーザ + '%' end) ) and ( 症状 like (case when @症状 = '' or @症状 is null then '%' else '%' + @症状 + '%' end) )
以上のSQLでうまく行かないのですから、パラメータには空文字でもnullでもない何かが入ってきていることになります。
それをSQL Server Profilerで確かめたかったのですが無理のようですので、代わりにSqlDataSourceのSelectingイベントを利用して、そのイベントハンドラ内でそれぞれのパラメータにどのような値が含まれているのかを確かめてみて下さい。この値によって、上記のSQLを変更する必要があります。パラメータを確かめるには、そのイベントハンドラ内でブレークポイントで止め、例えば、
e.Command.Parameters["@担当"].Value
の値を見ることでわかります。(参考)
SqlDataSource.Selecting イベント
http://msdn.microsoft.com/ja-jp/library/system.web.ui.webcontrols.sqldatasource.selecting(v=vs.110).aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-1★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
- 回答としてマーク masunona 2014年5月15日 0:59
-
【おまけ】
ConvertEmptyStringToNull="False" のままにしておく場合、SELECT クエリの WHERE 句を以下のようにするとうまく行くと思います。
WHERE ([ShipRegion] LIKE (CASE WHEN @ShipRegion<>'' THEN ('%' + @ShipRegion + '%') END)) OR ([ShipRegion] IS NULL AND @ShipRegion='')
項目が NULL の場合を抽出するには ([ShipRegion] IS NULL AND @ShipRegion='') という条件でないとダメなはずです。
なお、その場合、当然ですが、SqlDataSource.Selecting イベントのハンドラでパラメータを修正する必要はありません(ハンドラ自体が不要になります)。
- 回答としてマーク masunona 2014年5月15日 0:59
-
今頃になって気がつきましたが、条件で検索するだけではなくて、無条件で全件取得したい場合があるのですよね?
とすると、動的にクエリを組み立てるほかなさそうです。ストアド or C# のソースどちらでも作れると思いますが、とにかく今のような静的に作ったクエリでは無理そうです。そこまでやるのかどうか考えてください。
さらにもう一つ問題に気がつきました。こちらの方がやっかいかも。ComboBox のリストからユーザーが選択した項目で検索しても(即ち、抽出すべき項目は確定していて、あいまい検索など不要なのにもかかわらず)、あいまい検索になってしまうことです。
例えば[症状]に「重症」「重症ではない」というデータが含まれていたとします。
ユーザーが ComboBox のリストから「重症」を選択して検索をかけても、ComboBox にユーザーが「重症」と入力して検索をかけても、どちらのケースでもあいまい検索となって、「重症」「重症ではない」の両方が抽出されるということです。
ユーザーが「重症」だけを抽出したいのか、「重症」「重症ではない」の両方を抽出したいのかは、ユーザーの心を読まないと分かりません。
でも、それは不可能ですよね。
要件を見直したほうがいいのではないですか?
- 編集済み SurferOnWww 2014年4月27日 10:01 一部訂正
- 回答としてマーク masunona 2014年5月15日 0:59
-
特にSQLを動的に組み立てているわけではないんですね。ただその方がSQLインジェクションの可能性が無いので安心ですが・・・。
それはそれとして、like句を使う際の注意点がありますので、その対処をされているか確認させて下さい。like句で特殊な意味を持つ文字(というか記号)がありますので、本来であればそれらの文字をきちんとエスケープし、それらの文字が来ても検索できるようにすべきですが、その辺りは対処されていますでしょうか? ただ、ユーザー、担当の文字列にそんな特殊な文字を使うことは無いのであれば大丈夫ですが・・・。ちなみに、その特殊な文字は、[、%、_ です。
これらの文字のエスケープ方法は以下が参考になります。Like 句に対する SQL インジェクション対策
http://www.websec-room.com/2013/12/15/1307★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
- 回答としてマーク masunona 2014年5月15日 2:36
すべての返信
-
SQLDatasourceのSelectCommand="・・・
?
最終的にどのような検索条件になるのかがわからないのではっきりお答えできませんが、いずれにしても現在の条件文のところを書き直すだけであれば、あまり複雑なことはできません。しかし、条件に使う列が5つあり、そのうちの指定された列のみを使うのであれば、where句内でcaseを使うにことにより対応できます。例えば、
where 氏名 like case when @氏名<> '' then '%' + @氏名 + '%' else '%'end and 性別 like case when @性別<> '' then '%' + @性別' + '%' else '%'end and ・ ・ ・
のように書けます。@氏名が空文字であれば、結果的に、 氏名 like '%' になりますので、全てが検索対象となり、氏名による絞り込みが行われていないことと同等にになります。
ところで性別は揺るがないので、likeでなくても良いように思います。それとも揺らぐのでしょうか?
#追記。あ~、氏名は検索結果であり、抽出条件じゃないようですね。上記のSQL文で氏名を抽出条件に使っているのは誤りですね。
★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
- 編集済み trapemiyaModerator 2014年4月24日 4:50 SQL修正
-
ありがとうございます!
分かりずらいたとえで申し訳ありません。
実際にやってるのは性別ではなく,
氏名,県名,支店名,病気の症状,備考・・・
などなどなのです。
なので空欄含みのあいまい検索をしたいと思っています。
へんなたとえですみませんでした。。。
>条件に使う列が5つあり、そのうちの指定された列のみを使う
とは限らず,条件に使う列が5つくらいあって,
そのうち1つだけで絞り込む時もあれば5つ全部で絞り込む時もあります・・・。
やっぱり条件文だけでは難しいのですね。
ということで
ストアドプロシージャの基礎をネットで検索して勉強し始めています!
-
今さらながらですが・・・
GridView というのは ASP.NET の GridView ですよね? であれば、Visual Studio Express Edition のフォーラムより ASP.NET のフォーラムで質問した方が適切だと思います。
それから、質問の最初にご自分の環境(OS, .NET, IIS, Visual Studio のバージョン、使っているブラウザ、Web アプリケーションプロジェクトか Web サイトプロジェクトかなど)を書いてください。DB サーバーが何か聞かれて答えていますけど、最初に書いておけば無駄なやり取りが一回省けたはずです。さらに、最初の質問にアップされていたコードでやってみて、どこがどのようにうまく行かないか、期待していることと異なるのかを書いてください。
うるさいことを言うと思うかもしれませんが、タイムリーに的を得た回答が得やすくなるということで、質問者さんにメリットのあることです。回答者や一般閲覧者にも、余計なノイズが少なくより有益なスレッドになるということで、メリットがあることです。
- 編集済み 星 睦美 2014年4月24日 7:50 カテゴリの移動連絡受けました。
-
> やっぱり条件文だけでは難しいのですね。
> ということで
> ストアドプロシージャの基礎をネットで検索して勉強し始めています!そんなことはない(ストアドを使わなくても可能)と思います。
要するに、ユーザーが条件を TextBox や DropDownList に入力し、その結果を WHERE 句に設定した SELECT クエリで SQL Server から抽出し、抽出結果を GridView に表示できればいいのですよね?
実際の検索対象のテーブルのレコードの内容、検索対象項目、その項目に空、NULL、未定などがあるか否かなどの情報を書いていただけると、具体的なアドバイスができるかもしれません。
- 編集済み SurferOnWww 2014年4月24日 5:08 一部訂正
- 回答としてマーク masunona 2014年4月24日 6:09
- 回答としてマークされていない masunona 2014年4月24日 6:41
-
とは限らず,条件に使う列が5つくらいあって,
そのうち1つだけで絞り込む時もあれば5つ全部で絞り込む時もあります・・・。
やっぱり条件文だけでは難しいのですね。
ということで
ストアドプロシージャの基礎をネットで検索して勉強し始めています!
いえ、私が示したSQL文が、「そのうち1つだけで絞り込む時もあれば5つ全部で絞り込む時もあります・・・。」を解決する内容になっています。これ以上のことをやろうとするとストアドプロシージャでwhere部分を組み立てることになります。また、厳密に言えばlikeに指定する検索ワードはエスケープしなければなりません。なぜなら、検索ワードに例えば[を指定した場合、これはlikeにおける検索では特殊な意味を持ってしまい、[を検索ワードと見做してくれないからです。★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
- 回答としてマーク masunona 2014年4月24日 6:09
-
※質問内容をあらためて説明いただきました。
visual web developer 2010 Expressを使用してWeb アプリケーションプロジェクトを作成しています。
環境は以下です。
WEBサーバ:Windows Server 2008 R2 Standard
SQLServer:microsoft SQL SERVER 2008 R2
IISのバージョン:Version7.5.7600.16385
.NET のバージョン:Microsoft .NET Framework 4
使用ブラウザ:IE,Firefox,Chrome(すべて最新バージョン)
GridView(SQLDatasource)にデータの一覧を表示させ,
ajax cotrol toolkitでコンボボックスをいくつか配置し,
コンボボックスに入力された内容によりGridViewに絞り込みして表示させたいです。
データ一覧は以下のような感じです。
氏名 県名 支店名 症状 備考
AB 東京 A 無症
BC 神奈川 B 軽症 要フォロー
CD 千葉 B 重症
DE A 重症 電話する
EF 東京 D メールする
FG 神奈川 C ? なし
コンボボックスは
cmb県名 cmb支店名 cmb症状 cmb備考
と配置しています。
cmb県名に「東」と入力すると,GridViewに
氏名 県名 支店名 症状 備考
AB 東京 A 重症
EF 東京 D メールする
cmb支店名に「A」cmb症状に「症」と入力すると
氏名 県名 支店名 症状 備考
AB 東京 A 無症
DE A 重症 電話する
cmb県名に「東京」cmb支店名に「D」cmb備考に「メール」と入力すると
氏名 県名 支店名 症状 備考
EF 東京 D メールする
が抽出されるようにしたいです。
ご回答でいただいたのをヒントに以下のようにコードを書き直してみたのですが,
どこかが空白になっていると抽出できませんでした。
引き続きお気づきの点あればよろしくお願いいたします!
<asp:SqlDataSource ID="SqlDataSource1" runat="server"
ConnectionString="<%$ ConnectionStrings:aaaConnectionString3 %>" SelectCommand="SELECT ID, 氏名, ・・・ FROM MAIN where 県名 like case when @県名<> '' then '%' + @県名 + '%' else '%'end and 症状 like case when @症状<> '' then '%' + @症状 + '%' else '%'end"・・・>- 編集済み 星 睦美 2014年4月24日 7:59 ※オペレーターから注釈を追記
-
> ajax cotrol toolkitでコンボボックスをいくつか配置し,
Ajax Control Toolkit の ComboBox は日本語が使えますか?
最新版は分かりませんが、少なくとも自分が使っているもの(AjaxControlToolkit.dll のバージョンで 4.1.7.607)は非対応と記憶しています。
【追伸】
ここで質問を続けるのであれば、回答済みチェックマークは外した方がいいです。注目されにくくなって、回答も得にくくなりますので。
- 編集済み SurferOnWww 2014年4月24日 6:54 追伸追加
-
-
どこかが空白になっていると抽出できませんでした。
コンボボックスが空白の場合、nullが渡ってくるのかもしれませんね。もし、そうであれば、次のようにnull判定を追加してみて下さい。
@県名<> ''
を、
@県名<> '' and @県名 is not null
に、書き換える。実際にどのようなSQL文が発行されたかは、SQL Server Profiler というツールを使えば確認できます。時間がある時にでも調べてみて下さい。問題解決に役立ちます。
#新しく立てられたスレッドは削除、このスレッドはSQLの書き方のみで解決するかもしれませんが、ASP.NETの情報なども出てきましたし、GridViewにおける検索のサンプルになると判断しましたので、ASP.NETへの移動をリクエストしておきますね。
★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
- 編集済み trapemiyaModerator 2014年4月24日 7:15 引用訂正
-
> ただ予測入力はしてくれません(日本語非対応とのこと)。。。
候補を示してくれるところに ComboBox を使う意味があります。その機能が使えなければ DropDownList と同じです。
なので、とりあえずユーザー入力には DropDownList と TextBox を使うことで話をしませんか? (jQuery UI の ComboBox や Ajax Control Toolkit の AutoComplete を使う手もあるかもしれませんが、それはその次のステップで考えるとして)
まず、[県名]、[支店名] はあいまい検索などする必要はなくて、DropDownList から検索して設定してもらうだけで十分ではないですか? DB には NULL とか未定などが入っているかもしれませんが、それは DropDownList を使っても対応できると思います。SELECT クエリに LIKE 句は使わないで済むはずです。
[症状] も、項目が無症、軽症、重症、NULL、?等に限られるのであれば、[県名]、[支店名] と同様に DropDownList から検索して設定してもらうだけで十分ではないですか?
[氏名]、[備考] はそういうわけにはいかないでしょうから、それらは TextBox から候補を入力してもらって LIKE 句を使って検索するようにしてはどうですか。次のステップとして Ajax Control Toolkit の AutoComplete を使うことも考えられます。
上記の方針で OK かどうか連絡ください。
-
-
> コンボボックスをテキストボックスやドロップダウンリストに変更すれば
> 簡単に←?できるんでしょうか?DropDownList を使う項目はリストにある決まった項目しか入力しない(即ち、あいまい検索はしない)という分は簡単になるのは間違いないです。例えば、都道府県は 47 しかないのだから、固定したリストから選んでもらった方が当然簡単だと思うのですが。
それに、ユーザーに勝手な入力を入れられることにより起こるかもしれないトラブルの種を撒くことにもなりかねないので、可能な限り決まった項目しか入力できないようにした方がいいと思うのですが。
でも、「直接入力もドロップダウンリストからの選択も両方できるよう」と「すべての項目についてあいまい検索したい」という要件が絶対ならやむを得ないですね。
まず、trapemiya さんから提案のあった SELECT クエリを完成させて、それを省略しないでアップしてください。そして、それを実行した結果どうなったか詳しく書いてください。エラーメッセージが出たのなら、それを省略しないで書いてください。
前のレスで、
> やってみたのですが結果は同じでした・・・。
とありましたが、それでは何だか分かりません。どういう結果になったのか詳しく書いてください。
- 編集済み SurferOnWww 2014年4月25日 3:03 脱字追加
-
SurferOnWww 様
お返事いただきありがとうございます!
とりあえず,担当,確度,ユーザ,症状
についてあいまい検索をし,
ユーザ,症状
については空欄の場合でも抽出対象になるようにとSqlDataSourceにSELECT文を書いてみたつもりです。
<asp:SqlDataSource ID="SqlDataSource3" runat="server"
ConnectionString="<%$ ConnectionStrings:**ConnectionString3 %>" SelectCommand="SELECT ID, 氏名, 担当, 確度, ユーザ, 症状, **, **, **, **, **, **, **, **, **, ** FROM MAIN where (担当 LIKE '%' + @担当 + '%') AND (確度 LIKE '%' + @確度 + '%') AND ユーザ like case when (@ユーザ <> '' then '%' + @ユーザ + '%' else '%' end and 症状 like case when (@症状 <> '' then '%' + @症状 + '%' else '%' end">
<SelectParameters>
<asp:ControlParameter ControlID="cmbtanto" Name="担当" PropertyName="Text"
Type="String" ConvertEmptyStringToNull="False" />
<asp:ControlParameter ControlID="cmbshojyo" ConvertEmptyStringToNull="False"
Name="症状" PropertyName="SelectedValue" />
<asp:ControlParameter ControlID="cmbuser" ConvertEmptyStringToNull="False"
Name="ユーザ" PropertyName="SelectedValue" />
<asp:ControlParameter ControlID="cmbkakudo" Name="確度"
PropertyName="SelectedValue" ConvertEmptyStringToNull="False" />
</SelectParameters>
</asp:SqlDataSource>
担当,確度,ユーザ,症状すべてに未入力がないデータしか抽出できません。
trapemiyaさんに教えていただいたのをもとに,
@ユーザ <> ''のところを
(@ユーザ <> '' and @ユーザ is not null)
@症状 <> ''のところを
(@症状 <> '' and @症状 is not null)
としても結果は同じでした。
またよければ教えてください。
よろしくお願いいたします! -
ソースを元に戻されたのかもしれませんが、is not nullの追加や、カッコの数が合っていないなど中途半端です。正しくは以下のようになります。見やすいように整形しています。
where ( 担当 like (case when @担当 <> '' and @担当 is not null then '%' + @担当 + '%' else '%' end) ) and ( 確度 like (case when @確度 <> '' and @確度 is not null then '%' + @確度 + '%' else '%' end) ) and ( ユーザ like (case when @ユーザ <> '' and @ユーザ is not null then '%' + @ユーザ + '%' else '%' end) ) and ( 症状 like (case when @症状 <> '' and @症状 is not null then '%' + @症状 + '%' else '%' end) )
これでも動かない場合は、実際にどのようなSQL文が投げられているかSQL Server Profilerで確認してみて下さい。
Windows 7ですと、スタートボタン -> すべてのプログラム -> Microsoft SQL Server -> パフォーマンスツール の中にあります。
★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
- 回答としてマーク masunona 2014年5月15日 0:59
-
Hoshinaです
こんにちはプログラム全体を一気に完成させようとしないで,SQL文の動作確認だけを独立してされてはいかがでしょうか。
・データベースに格納されている現在のデータに対して,
・どのようなSQL文を発行したら,
・どのような結果が得られるか上記をしっかり確認してから,それからプログラム全体の完成を目指した方がよいように思えます。
これまでのやり取りを見る限りですと,SQLに関する知識が不足しているように見えます。
その状態では,中々進展しないと思います。また,上記の確認はSQL Server Management Studioがあれば簡単に確認できます。
それでは
-
SQL Server Profilerはインストールされていないようです。
「パフォーマンスツール」というフォルダ自体がありません。
?Express版なんですね。でしたら残念ながらありません。この辺りは重要な情報ですので、次からはここまで書くようにして下さいね。
さて、担当 like (case when @担当 <> '' and @担当 is not null
then '%' + @担当 + '%'
else '%'
end)この意味がわかりますでしょうか? @担当が空文字ではない、かつnullでもなければ、検索のキーワードで検索し、そうでなければ%のみを指定することによりすべてにマッチするようになりますから、実質、何も検索条件に指定していないのと同じになります。
流れで上のようなSQLになっていますが、以下のように書き換えても同様です。where ( 担当 like (case when @担当 = '' or @担当 is null then '%' else '%' + @担当 + '%' end) ) and ( 確度 like (case when @確度 = '' or @確度 is null then '%' else '%' + @確度 + '%' end) ) and ( ユーザ like (case when @ユーザ = '' or @ユーザ is null then '%' else '%' + @ユーザ + '%' end) ) and ( 症状 like (case when @症状 = '' or @症状 is null then '%' else '%' + @症状 + '%' end) )
以上のSQLでうまく行かないのですから、パラメータには空文字でもnullでもない何かが入ってきていることになります。
それをSQL Server Profilerで確かめたかったのですが無理のようですので、代わりにSqlDataSourceのSelectingイベントを利用して、そのイベントハンドラ内でそれぞれのパラメータにどのような値が含まれているのかを確かめてみて下さい。この値によって、上記のSQLを変更する必要があります。パラメータを確かめるには、そのイベントハンドラ内でブレークポイントで止め、例えば、
e.Command.Parameters["@担当"].Value
の値を見ることでわかります。(参考)
SqlDataSource.Selecting イベント
http://msdn.microsoft.com/ja-jp/library/system.web.ui.webcontrols.sqldatasource.selecting(v=vs.110).aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-1★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
- 回答としてマーク masunona 2014年5月15日 0:59
-
ConvertEmptyStringToNull="False" はどういう目的、思惑で入れてますか?
True (デフォルト) にしたらどうなりますか? 関係ないかもしれませんが、一度試していただけますか。
- 編集済み SurferOnWww 2014年4月25日 8:02 一部追記
-
【追伸】
よく考えてみたら、ConvertEmptyStringToNull を true(デフォルト)にするだけではダメでした。すみません。
そうする場合、(1) SELECT クエリの WHERE 句をそれにあわせて変更し、(2) さらにパラメータが null かどうかを判定し null の場合は DBNull.Value に置き換えるという操作が必要です。
以下のサンプルような感じです。(DB は Microsoft が無償で提供している Northwind.mdf の Orders テーブルを使用しています。抽出項目は簡略化のため ShipRegion 一つだけにしています。これで NULL の場合でも動くことは確認済みです)
上記 (1) については SqlDataSource2 の SelectCommand の WEHRE 句を、(2) についてはイベントハンドラ SqlDataSource2_Selecting を見てください。
<%@ Page Language="C#" %> <%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="asp" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server"> protected void SqlDataSource2_Selecting( object sender, SqlDataSourceSelectingEventArgs e) { object obj = e.Command.Parameters["@ShipRegion"].Value; if (obj == null) { e.Command.Parameters["@ShipRegion"].Value = DBNull.Value; } } </script> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title></title> </head> <body> <form id="form1" runat="server"> <asp:ToolkitScriptManager ID="ToolkitScriptManager1" runat="server"> </asp:ToolkitScriptManager> <asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:Northwind %>" SelectCommand="SELECT DISTINCT [ShipRegion] FROM [Orders] ORDER BY [ShipRegion]"> </asp:SqlDataSource> <asp:ComboBox ID="ComboBox1" runat="server" DataSourceID="SqlDataSource1" DataTextField="ShipRegion" DataValueField="ShipRegion"> </asp:ComboBox> <asp:Button ID="Button1" runat="server" Text="Button" /> <asp:SqlDataSource ID="SqlDataSource2" runat="server" ConnectionString="<%$ ConnectionStrings:Northwind %>" SelectCommand="SELECT [OrderID], [OrderDate], [ShipName], [ShipAddress], [ShipCity], [ShipRegion] FROM [Orders] WHERE (([ShipRegion] LIKE '%' + @ShipRegion + '%') OR ([ShipRegion] IS NULL AND @ShipRegion IS NULL))" OnSelecting="SqlDataSource2_Selecting"> <SelectParameters> <asp:ControlParameter ControlID="ComboBox1" Name="ShipRegion" PropertyName="SelectedValue" Type="String" /> </SelectParameters> </asp:SqlDataSource> <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataKeyNames="OrderID" DataSourceID="SqlDataSource2"> <Columns> <asp:BoundField DataField="OrderID" HeaderText="OrderID" InsertVisible="False" ReadOnly="True" SortExpression="OrderID" /> <asp:BoundField DataField="OrderDate" HeaderText="OrderDate" SortExpression="OrderDate" /> <asp:BoundField DataField="ShipName" HeaderText="ShipName" SortExpression="ShipName" /> <asp:BoundField DataField="ShipAddress" HeaderText="ShipAddress" SortExpression="ShipAddress" /> <asp:BoundField DataField="ShipCity" HeaderText="ShipCity" SortExpression="ShipCity" /> <asp:BoundField DataField="ShipRegion" HeaderText="ShipRegion" SortExpression="ShipRegion" /> </Columns> </asp:GridView> </form> </body> </html>
-
【おまけ】
ConvertEmptyStringToNull="False" のままにしておく場合、SELECT クエリの WHERE 句を以下のようにするとうまく行くと思います。
WHERE ([ShipRegion] LIKE (CASE WHEN @ShipRegion<>'' THEN ('%' + @ShipRegion + '%') END)) OR ([ShipRegion] IS NULL AND @ShipRegion='')
項目が NULL の場合を抽出するには ([ShipRegion] IS NULL AND @ShipRegion='') という条件でないとダメなはずです。
なお、その場合、当然ですが、SqlDataSource.Selecting イベントのハンドラでパラメータを修正する必要はありません(ハンドラ自体が不要になります)。
- 回答としてマーク masunona 2014年5月15日 0:59
-
今頃になって気がつきましたが、条件で検索するだけではなくて、無条件で全件取得したい場合があるのですよね?
とすると、動的にクエリを組み立てるほかなさそうです。ストアド or C# のソースどちらでも作れると思いますが、とにかく今のような静的に作ったクエリでは無理そうです。そこまでやるのかどうか考えてください。
さらにもう一つ問題に気がつきました。こちらの方がやっかいかも。ComboBox のリストからユーザーが選択した項目で検索しても(即ち、抽出すべき項目は確定していて、あいまい検索など不要なのにもかかわらず)、あいまい検索になってしまうことです。
例えば[症状]に「重症」「重症ではない」というデータが含まれていたとします。
ユーザーが ComboBox のリストから「重症」を選択して検索をかけても、ComboBox にユーザーが「重症」と入力して検索をかけても、どちらのケースでもあいまい検索となって、「重症」「重症ではない」の両方が抽出されるということです。
ユーザーが「重症」だけを抽出したいのか、「重症」「重症ではない」の両方を抽出したいのかは、ユーザーの心を読まないと分かりません。
でも、それは不可能ですよね。
要件を見直したほうがいいのではないですか?
- 編集済み SurferOnWww 2014年4月27日 10:01 一部訂正
- 回答としてマーク masunona 2014年5月15日 0:59
-
SurferOnWwwさんへの返信というわけではなく、私のつぶやき的な書き込みです。よって、以下、少し汎用的な内容になります。
質問者のmasunonaさんが検索に関してどのような仕様を求められているかですが、全てをあいまい検索するという仕様であれば問題ないように思います。ちょうど、Windowsのエクスプローラーの検索などがそうですが、一度入力したワードは覚えていて、ドロップダウンより選ぶことができます。これと同じように、入力履歴ではなく、よく検索するワードをドロップダウンに予め仕込んでおくというのもありのような気がします。このように、どのような手段を経たかにはかかわらず、そこに入力されているワードであいまい検索されるという仕様は、ケースによっては適しているように思います。
実際、ASP.NETではありませんが、上記のように実装することは私はよくあります。一度入力したワードは履歴が取られ、アプリケーション全体でそれを使用できます。例えば契約番号などをそのようにしておくと、画面が切り替わってもドロップダウンから選ぶだけで済みます。
また、検索に関しても、入力されたワードが語頭一致、語尾一致、あいまい、完全一致のオプションを選んで検索するように実装することもよくあります。また、複数ワードを入力した際に、AND検索、OR検索などをサポートすることもあります。この辺りになるとSQL文を動的に組み立てなければ無理ですが、その場合でも、できるだけデータベースが用意しているエスケープの処理を利用するようにすると確実です。
そもそもあいまい検索自体が、その名の通り検索結果があいまいなので、検索結果に全て目を通して人間で判断する必要があります。よって、あいまい検索の項目を増やし過ぎると検索結果の検証が難しくなるように思います。Bingなどの検索エンジンで入力する項目はあくまで一つです。ここの違いは気に留めて置いた方が良いように思います。
★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
- 編集済み trapemiyaModerator 2014年4月28日 2:05 追記
-
みなさま
たくさんお返事いただきありがとうございます!
今日は時間がなくばーっと急いで検証してみました。
WHERE ([ShipRegion] LIKE (CASE WHEN @ShipRegion<>'' THEN ('%' + @ShipRegion + '%') END))
OR ([ShipRegion] IS NULL AND @ShipRegion='')
SQL文の4つの抽出条件を上記に書き換えてみたところ,
”症状”に"重症"と入力すると,”ユーザ"が空欄のものが無事に抽出されました!
やった!と思って
"症状"に"重症"と入力したのに加え"担当"に"山田"と入力すると
"担当"に"山田"となっている全件と,"症状"に"重症"となってる全件が抽出されてしまいました。
"担当"が"山田"かつ"症状"が"重症"が抽出されてほしかったです・・・。
皆様からいただいたお話をもとに
休み明けもう少し時間を取って考えてみます。
取り急ぎのお礼とさせていただきます。 -
-
もう一度書きますが、以下の件はどうするんですか?
今頃になって気がつきましたが、条件で検索するだけではなくて、無条件で全件取得したい場合があるのですよね?
とすると、動的にクエリを組み立てるほかなさそうです。ストアド or C# のソースどちらでも作れると思いますが、とにかく今のような静的に作ったクエリでは無理そうです。そこまでやるのかどうか考えてください。
さらにもう一つ問題に気がつきました。こちらの方がやっかいかも。ComboBox のリストからユーザーが選択した項目で検索しても(即ち、抽出すべき項目は確定していて、あいまい検索など不要なのにもかかわらず)、あいまい検索になってしまうことです。
例えば[症状]に「重症」「重症ではない」というデータが含まれていたとします。
ユーザーが ComboBox のリストから「重症」を選択して検索をかけても、ComboBox にユーザーが「重症」と入力して検索をかけても、どちらのケースでもあいまい検索となって、「重症」「重症ではない」の両方が抽出されるということです。
ユーザーが「重症」だけを抽出したいのか、「重症」「重症ではない」の両方を抽出したいのかは、ユーザーの心を読まないと分かりません。
でも、それは不可能ですよね。
要件を見直したほうがいいのではないですか?
-
上の私の問いかけに答えていただけませんか?
ここは質問者さん専用の Q&A サイトではなく、開発者が情報交換をするフォーラムと考えていただけると幸いです。
一方的に自分の聞きたいことを聞くだけではなく、回答者とやり取りをしながら進めていった方が、的を得た回答が早く得られて、結局は問題の解決がより早く容易になって、質問者さんのメリットになることが多いはずです。
例えば、結局動的にクエリを組み立てるほかないという話になれば、どのように動的にクエリを組み立てるかの話を先にした方が良さそうです。
さらに、2 つ目の問題が許容できなければ、根本的なところから見直しが必要かもしれません。
そのような場合は、Selectingイベントを利用して調べるうんぬんの話をしてもあまり意味はないわけですから。
-
大変遅くなってしまい申し訳ありません。
要件を見直す予定は今のところないようです。
たしかに検索しずらくなる点もあるかと思いますが
当面はこの方法でやっていきたいとのことです。
色々検証してみたところ,全件表示のところがうまくいっていない様子です。
<asp:SqlDataSource ID="SqlDataSource3" runat="server"
ConnectionString="<%$ ConnectionStrings:BKANMTConnectionString3 %>"
SelectCommand="SELECTSELECT * FROM データ名 WHERE ユーザ like '%'★">
<SelectParameters>
<asp:ControlParameter ControlID="コンボボックス" Name="ユーザ" PropertyName="Text"
Type="String" ConvertEmptyStringToNull="False" />
</SelectParameters>
としてみたのですが,ユーザが空欄でないものだけが抽出されてしまいました。
★のところを
WHERE ユーザ like '%' or ユーザ is null
とすれば全件表示させることはできるのですが,
これをcase文に組み込む方法が分からず・・・
明日もやってみます。 -
【追伸】
「動的にクエリを組み立てる」というところが理解されてないかもしれないので、そのあたりを補足しておきます。
3 つの条件、すなわち (1) 全件を抽出、(2) 空白(DB 上では NULL)を抽出、(3) あいまい検索・・・が今回必要と理解しています。
その理解が正しいとして、質問者さんの直近のレスを例にとって [ユーザ] で条件検索する場合、(1) ~ (3) それぞれで SELECT クエリは以下のようになるはずです。
(1) 全件: SELECT * FROM データ名
(2) NULL: SELECT * FROM データ名 WHERE [ユーザ] IS NULL AND @ユーザ IS NULL
(3) 曖昧: SELECT * FROM データ名 WHERE [ユーザ] LIKE '%' + @ユーザ + '%'「動的にクエリを組み立てる」というのは、ComboBox の SelectedValue(上の例では「@ユーザ」が該当)に応じて、上記のような WHERE 句を変化させた SELECT クエリを作るということです。C# 側のコードまたはストアドどちらでも可能だと思います。
なお、(1) のケースを「@ユーザ」の値から判断するは今のコードのままではできませんが、例えば以下のように ListItem を ComboBox に追加しておけば、「@ユーザ」が "ALL" かどうかで判断できるはずです。
<asp:ComboBox ID="ComboBox1" runat="server" DataSourceID="SqlDataSource1" DataTextField="ShipRegion" DataValueField="ShipRegion" AppendDataBoundItems="True"> <asp:ListItem Value="ALL" Selected="True">ALL</asp:ListItem> </asp:ComboBox>
-
SurferOnWww 様
ありがとうございます!
結局ストアドプロシージャに挑戦してみようと思い,勉強始めました。
手始めに,コンボボックス「ユーザ」が空欄の場合は全件表示,
言葉が入力されている場合はあいまい検索するところから始めようと思い,
SQLServerのストアドプロシージャで
@user VARCHAR(10) = ''
AS
IF @user = ''
BEGIN
SELECT *
FROM dbo.MAIN
ORDER BY ID DESC
END
ELSE
BEGIN
SELECT *
FROM dbo.MAIN
WHERE ユーザ LIKE '%' + @user + '%' ORDER BY ID DESC
END
と記述し,「SelectUser」と名前をつけて実行してみたところ,
@userが空欄の場合は全件表示で,@userに言葉を入れるとあいまい一致で抽出できました。
やった!と思い,GridViewのSQLDatasourceに
<asp:SqlDataSource ID="SqlDataSource3" runat="server"
ConnectionString="<%$ ConnectionStrings:XXXConnectionString3 %>"
SelectCommand="SelectUser" SelectCommandType="StoredProcedure" CancelSelectOnNullParameter="False">
<SelectParameters>
<asp:ControlParameter ControlID="cmbuser" Name="user" PropertyName="SelectedValue"
Type="String" />
</SelectParameters>
として検索してみました。
コンボボックスに言葉を入力して検索した場合はうまく抽出できたのですが,
空欄の場合は結果が0件になってしまいました。
ただ,
IEのコンボボックスに,全角スペースを入力すると全件表示してくれます。
半角スペースではだめだし,
Firefox,Chromeでは,全角スペースでも半角スペースでも0件になってしまいます。
ストアドの
=''のところを
=NULLと書き換えると,SQLServerでストアドを実行した時点で全件表示されず0件になってしまいます。
=''のところを
is null に書き換えると,構文エラーになってしまいます。
GridViewのSQLDatasourceの
CancelSelectOnNullParameter="False"のところを
CancelSelectOnNullParameter="True"にしても,結果は同じでした。
なにかお気づきの点ありましたら教えてくださるとうれしいです。
お願いいたします! -
> これからいよいよ本題に入りたいと思います。
その際は、最終的にどうしたいのか、やりたいこと・望む形を明示してくださいね。
特に、先の私のレスの以下の点を確認願います。
3 つの条件、すなわち (1) 全件を抽出、(2) 空白(DB 上では NULL)を抽出、(3) あいまい検索・・・でよいのか?
上記 (1) のケースを「@ユーザ」の値から判断するため、Value="ALL" の ListItem を ComboBox に追加し、「@ユーザ」が "ALL" かどうかで判断することでよいのか?
でないと、こちらとしては、また話が 2 転 3 転するのではと思ってしまい、イマイチやる気がおきないです。
- 編集済み SurferOnWww 2014年5月9日 3:06 誤記訂正
-
お礼が大変遅くなってしまって申し訳ございませんでした。
ストアドプロシージャで無事解決しました!
以下のように記述し,やっとのことで無事に理想の動きをしております。
ALTER PROCEDURE [dbo].[Selectcmd]
@tanto VARCHAR(100) = NULL,
@kakudo int = NULL,
@shojyo VARCHAR(100) = NULL,
@user VARCHAR(100) = NULL
AS
BEGIN
SELECT *
FROM dbo.MAIN
WHERE
確度=Case when @kakudo is null then 確度 else @kakudo end
and ((@shojyo IS NULL) OR (@shojyo IS NOT NULL) AND (症状 LIKE '%'+@shojyo+'%'))
and ((@user IS NULL) OR (@user IS NOT NULL) AND (ユーザ LIKE '%'+@user+'%'))
and ((@tanto IS NULL) OR (@tanto IS NOT NULL) AND (担当 LIKE '%'+@tanto+'%'))
END
確度だけはあいまいにせず,そのほかはあいまいで抽出できるようになりました。
検索条件がすべて空の場合については「すべて表示」ボタンを別に作成してプログラムを組みました。
大変長い間お付き合いいただきまことにありがとうございました。 -
特にSQLを動的に組み立てているわけではないんですね。ただその方がSQLインジェクションの可能性が無いので安心ですが・・・。
それはそれとして、like句を使う際の注意点がありますので、その対処をされているか確認させて下さい。like句で特殊な意味を持つ文字(というか記号)がありますので、本来であればそれらの文字をきちんとエスケープし、それらの文字が来ても検索できるようにすべきですが、その辺りは対処されていますでしょうか? ただ、ユーザー、担当の文字列にそんな特殊な文字を使うことは無いのであれば大丈夫ですが・・・。ちなみに、その特殊な文字は、[、%、_ です。
これらの文字のエスケープ方法は以下が参考になります。Like 句に対する SQL インジェクション対策
http://www.websec-room.com/2013/12/15/1307★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
- 回答としてマーク masunona 2014年5月15日 2:36