
ACCESSのフォームで最もよく使われるのが検索フォームです。
多くのレコードが有るとその中から特定のレコードを効率よく抽出したいものです。
この記事では、ACCESSでの検索フォームの作成方法を、VBAのコードも含めてご紹介します。
検索機能だけでなく、検索フォームにあると便利な他の機能も含めて6つの機能をご説明しますね。
なお、ご自身で最初から作るのが難しいという方は、この記事で紹介した検索フォームのファイルを販売していますので、記事の最後をご覧ください。
こんにちは。
はこにわガジェット (@hakoniwagadget) です。
ACCESSを使った売上管理、顧客管理などのデータベース開発を行っています。
ACCESSは使いこなすために少しスキルが必要なものの、うまく活用すればExcelよりも業務の効率化が図れます。
この記事ではACCESSで実際に作成したフォームやレポートを、その作成方法と共にご紹介していきます。
ACCESSで作成した検索フォーム(完成形)
まず、作成した検索フォームの機能をご紹介します。
画面は下記の通りです。

これは、売上データを検索するためのフォームで、以下の機能を実装しています。
- 検索条件を設定して検索ボタンを押すと、条件に合ったレコードのみが表示される
- 受注日、売上日、販売チャネルなどの複数の検索条件を組み合わせて検索できる
- 条件クリアボタンを押すと検索条件がクリアされ、レコード一覧も元に戻る
- 検索結果に表示されたレコードのみの売上金額合計が表示される
- 受注日、売上日、顧客名ラベルを押すとそれぞれの値でレコードが並べ替えされる
- 作業中のみボタンを押すと売上前の作業中レコードのみ表示される
- レコード一覧で特定のレコードの売上IDをダブルクリックすると、詳細が表示される
それでは、作り方を順に見ていきましょう。
フォームの作成
デザイン
まず、フォーム自体を作成します。
フォームはデザインビューで作成していきます。
今回作成したフォームのデザインビューが以下の画面です。

デザインビューでフォームを作成するには、ACCESSメニューのフォームデザインを選択します。

フォームデザインで作成した空白のフォームに様々なコントロールを配置してきます。
まず、フォーム全体のビューを設定します。
プロパティシートを表示し、フォーム全体を選択して「既定のビュー」を帳票フォームにしましょう。
帳票フォームは詳細セクション(画面中央)に1レコード分のレイアウトを作成し、それを表のように繰り返すレイアウトです。
今回は検索結果を一覧表示しますので帳票フォームを選択します。

検索条件はフォーム上部の「フォームヘッダー」に作成します。

詳細セクションはスクロールすると下へ移動していきますが、フォームヘッダーは位置が固定なので検索条件等のずっと表示しておきたいものを配置するのに便利です。
こちらに検索条件を入力するためのテキストボックスやコンボボックスを設定していきます。
レコードソースの設定
デザインビューでフォーム全体を指定して、プロパティシートを見ると、レコードソースを設定することができます。
フォームのレコードソースとは、そのフォームに表示する元データが入っているテーブルもしくはクエリを指定します。
このデータベースでは売上データが入ったテーブルは「TRN_売上」というテーブルです。
ちなみに、TRNはトランザクションの略です。
私独自の命名規則ですが、処理毎に蓄積されていくデータを入れるテーブルには「TRN_〇〇」と名付けています。
逆に最初から設定しておくデータはマスターデータとして「MST_〇〇」と名付けています。
このあたりの命名規則はご自身が気に入ったものを使っていただけばよいのですが、統一ルールにしておくと間違いが少ないでしょう。
命名規則については以下の記事も参照ください。

話を戻します。
実は先ほどのフォームではレコードソースを「TRN_売上」にはしていません。

レコードソースは「LIST_売上」という「TRN_売上」を元にした選択クエリにしています。
「TRN_売上」をそのままフォームのレコードソースにしてしまうと不便なことがあるからです。
下記は「TRN_売上」のデータです。
リレーショナルデータベースなので、販売チャネルIDや商品IDは他のテーブルと連携するために、キー値である数値が入っています。
この数値が別の「MST_販売チャネル」や「MST_商品」テーブルと連携することで、商品IDの1は、〇〇〇〇という商品名というように紐づけができていくのです。

フォームのレコードソースに「TRN_売上」自体を使ってしまうと、この値の読み替えができず、フォームの表示が下記のようになってしまいます。

販売チャネルや顧客名、商品名が数値になってしまったのがお分かりいただけると思います。
これでは使いにくいですよね。
コンボボックスによる値の読み替え
これを回避する方法もあるにはあります。
例えば販売チャネルIDのコントロールを右クリックして、コントロールの種類の変更からコンボボックスを選択します。

そして、コンボボックスのプロパティのデータタブで「値集合ソース」に「MST_販売チャネル」を選択します。

更に、書式タブで、列数を2、列幅を0cm、3cmと設定します。

これは、コンボボックスに表示する「MST_販売チャネル」テーブルの値を2列分にするが、1列目は幅を0cmにして表示せず、2列目のみ表示するという設定です。なぜこんなことをするかというと、「MST_販売チャネル」の1列目は販売チャネルIDで数値なので、コンボボックスには必要が無いからです。なお、「MST_販売チャネル」の2列目には販売チャネル名がテキストで入っています。
コンボボックスの作成方法は以下の記事に詳細に書いておりますので、興味のある方はご覧ください。

では、設定を変えた後のフォームを見てみましょう。

販売チャネルの部分がコンボボックスになり、数値ではなく販売チャネル名が表示されていることが分かると思います。
この方法であれば、レコードソースがテーブルであっても必要なデータを見せることができます。
ただ、問題はコンボボックスになってしまうことです。
一般的に検索フォームはデータ入力をさせるフォームではないので、必要な情報が一画面に収まることが重視されます。
コンボボックスにしてプルダウンボタンがつくことで横幅が長くなり一画面に収めにくくなることと、見栄えが悪くなるという問題があります。
完成形の画面を再度提示しましょう。
コンボボックスにしていないので見やすいですよね。

ではどうしたらこの形を実現できるか。
答えは、レコードソースにクエリを使う、です。
レコードソースにクエリを使う
まず、レコードソースに使用するクエリを作成しましょう。
クエリのデザインビューで作成します。

下記のように、元となるテーブル「TRN_売上」からIDで連結したテーブルを接続し、販売チャネル名、顧客名、商品名などのフィールドを各テーブルからクエリに追加していきます。

上記の画面では、売上IDや受注などはテーブルが「TRN_売上」を参照しているのに対して、販売チャネル名は「SORT_販売チャネル」から持ってきていることがわかります。
クエリが作成できたら、検索フォームのデザインビューでレコードソースを変更します。

レコードソースにクエリを設定すれば、クエリに販売チャネル名、顧客名、商品名などのフィールドが存在していますので、それらをそのままフォームに表示することが可能です。
レコード一覧が分かりやすい名前で表示できるようになりました。

ここまででフォームの見た目が作れました。
各種機能の開発方法
検索条件を設定して検索ボタンを押すと、条件に合ったレコードのみが表示される
ここからはこのフォームに実装した、7つの機能の開発方法を説明してきます。
まずは検索フォームの基本である、検索条件を設定して検索ボタンを押すと条件に合ったレコードのみが表示される、という機能です。
このフォームでは各種検索条件を入力した後で、「検索」ボタンを押すと結果が表示されるようにしてあります。
そのため、フォームのデザインビューから検索ボタンのプロパティを見ると、クリック時イベントにイベントプロシージャを設定しています。

イベントプロシージャーとは、ACCESSで特定の操作を自動化するためのプログラムです。
では、クリックした際のイベントプロシージャをVisual Basic Editor(VBE)で見てみましょう。
Dim filter_txt As String
Dim filter_states As String
Private Sub 検索_ボタン_Click()
filter_txt = ""
'FROM、TOの前後不備チェック
If Me!受注日検索FROM <> "" And Me!受注日検索TO <> "" Then
If Me!受注日検索FROM > Me!受注日検索TO Then
MsgBox "受注日の期間指定が不適切です。再度入力してください。", vbCritical + vbOKOnly, "受注日期間指定不備"
Exit Sub
End If
End If
If Me!売上日検索FROM <> "" And Me!売上日検索TO <> "" Then
If Me!売上日検索FROM > Me!売上日検索TO Then
MsgBox "売上日の期間指定が不適切です。再度入力してください。", vbCritical + vbOKOnly, "売上日期間指定不備"
Exit Sub
End If
End If
'受注期間
If Me!受注日検索FROM <> "" Then
If Me!受注日検索TO <> "" Then
filter_txt = "受注日 >= #" & Me!受注日検索FROM & "# and 受注日 <= #" & Me!受注日検索TO & "#"
Else
filter_txt = "受注日 >= #" & Me!受注日検索FROM & "#"
End If
ElseIf Me!受注日検索TO <> "" Then
filter_txt = "受注日 <= #" & Me!受注日検索TO & "#"
End If
'売上期間
If Me!売上日検索FROM <> "" Then
If Me!売上日検索TO <> "" Then
If filter_txt = "" Then
filter_txt = "売上日 >= #" & Me!売上日検索FROM & "# and 売上日 <= #" & Me!売上日検索TO & "#"
Else
filter_txt = filter_txt & " and 売上日 >= #" & Me!売上日検索FROM & "# and 売上日 <= #" & Me!売上日検索TO & "#"
End If
Else
If filter_txt = "" Then
filter_txt = "売上日 >= #" & Me!売上日検索FROM & "#"
Else
filter_txt = filter_txt & " and 売上日 >= #" & Me!売上日検索FROM & "#"
End If
End If
ElseIf Me!売上日検索TO <> "" Then
If filter_txt = "" Then
filter_txt = "売上日 <= #" & Me!売上日検索TO & "#"
Else
filter_txt = filter_txt & " and 売上日 <= #" & Me!売上日検索TO & "#"
End If
End If
'販売チャネル
If Me!販売チャネル検索 <> "" Then
If filter_txt = "" Then
filter_txt = "販売チャネルID = " & Me!販売チャネル検索
Else
filter_txt = filter_txt & " and 販売チャネルID = " & Me!販売チャネル検索
End If
End If
'顧客名
If Me!顧客名検索 <> "" Then
If filter_txt = "" Then
filter_txt = "顧客名 like '*" & Me!顧客名検索 & "*'"
Else
filter_txt = filter_txt & " and 顧客名 like '*" & Me!顧客名検索 & "*'"
End If
End If
'商品名
If Me!商品名検索 <> "" Then
If filter_txt = "" Then
filter_txt = "商品ID = " & Me!商品名検索
Else
filter_txt = filter_txt & " and 商品ID = " & Me!商品名検索
End If
End If
'ステータス
Select Case Me!作業中検索
Case True
Select Case Me!完了検索
Case True
Select Case Me!キャンセル検索
Case True
Case False
filter_states = "ステータスID <= 2"
End Select
Case False
Select Case Me!キャンセル検索
Case True
filter_states = "ステータスID <> 2 "
Case False
filter_states = "ステータスID = 1"
End Select
End Select
Case False
Select Case Me!完了検索
Case True
Select Case Me!キャンセル検索
Case True
filter_states = "ステータスID >= 2"
Case False
filter_states = "ステータスID = 2"
End Select
Case False
Select Case Me!キャンセル検索
Case True
filter_states = "ステータスID = 3 "
Case False
End Select
End Select
End Select
If filter_states <> "" Then
If filter_txt <> "" Then
filter_txt = filter_txt & " and " & filter_states
Else
filter_txt = filter_states
End If
End If
Debug.Print filter_txt
Me.Filter = filter_txt
Me.FilterOn = True
Me.OrderBy = "受注日 DESC"
Me.OrderByOn = True
End Sub
非常に長いプログラムですが、基本構造は上記の通りです。
「filter_txt」という文字列型の変数を定義して、フォームの抽出条件の設定値に応じて、「filter_txt」にフィルター条件を追記していきます。
例えば最初の受注期間だと、条件分岐を使用して
・「受注日検索FROM」と「受注日検索TO」の両方に値が入っていれば、その間
・「受注日検索FROM」のみに値が入っていれば、「受注日検索FROM」以降の日
・「受注日検索TO」のみに値が入っていれば、「受注日検索TO」以前の日付
という条件式を「filter_txt」に書き込みます。
そのうえで最後に、
Me.Filter = filter_txt
Me.FilterOn = True
と設定して、フォーム自体のフィルター条件に「filter_txt」を設定するのです。
これによって指定した条件に合ったレコードのみが表示されます。
あとは必要な抽出条件を追加していくだけです。
Filterプロパティを使った抽出条件の書き方については以下の記事を参考にしてください。

受注日、販売チャネルなどの複数の検索条件を組み合わせて検索できる
2つ目の機能である複数の検索条件を組み合わせて検索する方法です。
こちらは前述のコードの中で、販売チャネルの部分を見てください。
If filter_txt = "" Then
という条件文があります。
これは、「filter_txt」が空、つまりその前にある受注日検索に何も条件が設定されていなかった場合、という意味です。
「filter_txt」が空であれば、単純に「filter_txt」に販売チャネルの条件を入れればよいので、
filter_txt = "販売チャネルID = " & Me!販売チャネル検索
となります。
一方、「filter_txt」が空でない、つまりその前にある受注日検索で条件が設定されていた場合は、二つの条件文を「and」でつなぐ必要があります。そのため、
filter_txt = filter_txt & " and 販売チャネルID = " & Me!販売チャネル検索
となります。
後者では元々の「filter_txt」の文字列(つまり受注日検索の条件文)を活かしつつ、そこに「and」以降の条件を追記していることがお分かりいただけると思います。
このように設定することで検索条件が増えても、最終的に「filter_txt」を使ってフィルターを行えば希望通りのレコード抽出が可能です。
条件クリアボタンを押すと検索条件がクリアされ、レコード一覧も元に戻る
検索を続けていくと、いったん条件をリセットして最初の状態に戻したくなる時があります。
そのために、条件クリアボタンを設けています。
検索条件がクリアされるだけでなく、レコード一覧も初期状態に戻ります。
条件クリアボタンのクリック時イベントに以下のイベントプロシージャを設定しています。
Private Sub 条件クリア_ボタン_Click()
Me!受注日検索FROM = ""
Me!受注日検索TO = ""
Me!売上日検索FROM = ""
Me!売上日検索TO = ""
Me!販売チャネル検索 = ""
Me!顧客名検索 = ""
Me!商品名検索 = ""
Me!作業中検索 = True
Me!完了検索 = True
Me!キャンセル検索 = False
filter_txt = ""
filter_states = ""
Me.FilterOn = False
Me.OrderBy = "受注日 DESC"
Me.OrderByOn = True
End Sub
いくつか並んでいるMe!〇〇 = “”はフォームにある検索条件を設定するコントロールの値をクリアするためのコードです。
これで検索条件を初期状態に戻したうえで、
Me.FilterOn = False
でフィルターを解除してすべてのレコードを表示します。
更に、
Me.OrderBy = "受注日 DESC"
Me.OrderByOn = True
で受注日の降順(新しいものが一番上)で並び替えます。
検索結果に表示されたレコードのみの売上金額合計が表示される
画面の右上には売上金額の合計を表示しています。
検索フォームでは、こうした合計値は検索結果に連動し、表示されたレコードのみの合計値を表示できると便利です。
売上金額合計のテキストボックスをデザインビューで見ると以下のようになっています。

コントロールソースを
=Sum([売上金額])
と設定してあります。
Sum関数はフォームやレポートなどで表示されたレコードの合計を計算する関数です。
特に条件を指定しなくても常に表示されたレコードの合計のみを表示するため、検索フォームで利用すると便利です。
Sum関数の引数には[売上金額]と書いてありますが、フォームやクエリ等でコントロールを指定する場合は[]で囲う必要がありますので注意してください。
また、Sum関数と類似した関数にDSum関数があります。
検索フォームではSum関数を使う方が便利ですが、使い分けが気になる方は以下の記事もご覧ください。

受注日、売上日、顧客名ラベルを押すとそれぞれの値でレコードが並べ替えされる
特定のフィールドの値で並べ替えを頻繁に行う場合は、並び替え専用機能を設けておくと便利です。
今回の検索画面は基本的に受注日の降順で並び替えをしていますが、売上日や顧客名でで並べ替えができるようにしました。
受注日ラベルのクリック時イベントにイベントプロシージャを設定します。
並べ替えの機能を設定する際は、ラベルをクリックすると並べ替えされるようにしておくのが直感的にわかりやすいのでお勧めです。

受注日クリック時イベントには以下のイベントプロシージャを設定しています。
If Me.OrderBy = "受注日" Then
Me.OrderBy = "受注日 DESC"
Me.OrderByOn = True
Else
Me.OrderBy = "受注日 ASC"
Me.OrderByOn = True
End If
ここではMe.OrderBy句を使って並べ替えを行っています。
Me.OrderBy句は=の後に文字列で並べ替え条件を設定し、Me.OrderByOn = Trueにすることで並べ替えが実行されます。
Me.OrderBy = "受注日 ASC"
が受注日の昇順での並べ替えです。ASCを省略した場合も昇順になります。
一方、
Me.OrderBy = "受注日 DESC"
が受注日の降順での並べ替えです。
日付の場合は降順(新しいものが上)に来る並び順を既定値にしておく方が便利でしょう。
If文で分岐させているのは、昇順、降順をクリックするだけで切り替えられるようにするためです。
Me.OrderByがすでに受注日、つまり受注日昇順だった場合は受注日降順で並べ替え、それ以外のケースでは受注日昇順で並べ替えできるようにしています。
これと同じような機能を売上日ラベル、顧客名ラベルにも実装しています。
OrderBy句を使った並べ替え方法は以下の記事でも解説しています。

作業中のみボタンを押すと売上前の作業中レコードのみ表示される
よく利用する検索条件は、条件を指定して検索ボタンを押さなくても1ボタンクリックで検索できるようにしておくと便利です。
今回は、作業中のレコードのみを簡単に抽出するために専用のボタンを設けました。
作業中のみボタンのクリック時イベントに以下のイベントプロシージャを設定しています。
Private Sub 作業中のみ_ボタン_Click()
Me.Filter = "ステータスID = 1"
Me.FilterOn = True
End Sub
ステータスID = 1 が、今回のデータベースでは作業中を表すステータスIDになっています。
Filterプロパティを使ってステータスIDが1のレコードのみを抽出できるようになっています。
レコード一覧で特定のレコードの売上IDをダブルクリックすると、詳細が表示される
最後に非常によく使う機能です。
検索フォームは一画面に収めるために、レコードの中でも全フィールドを表示するのではなく重要なフィールドだけを表示することが多くなります。
となると、検索画面でそのままレコードを更新したりすることが難しくなります。
そのため、入力用のフォームは別途作成しておき、検索フォームから全フィールドが表示される入力用フォームを呼び出す、という仕様にするのが便利です。
私はこの機能を作成する際、検索フォームでID(キー値)をダブルクリックすると入力フォームが開く、という仕様を基本としています。
そのため、売上IDにダブルクリック時イベントを設定します。

設定されているイベントプロシージャはこちらです。
Private Sub 売上ID_DblClick(Cancel As Integer)
If IsNull(Me!売上ID) = False Then
DoCmd.OpenForm "売上入力", , , "売上ID = " & Me!売上ID
Else
DoCmd.OpenForm "売上入力", , , , acFormAdd
End If
End Sub
IF文で分岐させていますが、条件文が IsNull(Me!売上ID) = False ですので、売上IDがNullでない場合、つまり何からの数値が売上IDに入っている場合が上段になります。
DoCmd.OpenForm "売上入力", , , "売上ID = " & Me!売上ID
というコードで、売上入力フォームを開くのですが、その際WHERE句で売上ID = Me!売上ID と指定してダブルクリックされた売上IDと同じ売上IDのレコードを開きます。
一方でIF条件がFALSE、つまり、売上IDがNULLのレコードをダブルクリックした場合は、
DoCmd.OpenForm "売上入力", , , , acFormAdd
で新規レコードで売上入力フォームを開く、という設定にしてあります。
以上、ACCESSの検索フォーム作成方法をご紹介しました。
この記事の内容を実際に試したい方へ
本記事の内容を実際にACCESSを動かしながら確認したいという方向けに、記事で紹介した機能が実装されたサンプルファイルを販売しています。記事内で解説しているテーブル、クエリ、フォーム、レポート、VBAのプロシージャなどをそのまま動く形で実装しました。
学習用としてはもちろん、加工して自社業務に利用することも可能です。
ご興味のある方は以下からご覧ください。

ACCESSを使いこなせば、業務の効率化や自動化が実現できます。
しかし、自分でACCESSを学ぶには時間がない、難しそうで不安、という方も多いでしょう。
そんな時は、ACCESS開発歴20年以上、過去に300以上のACCESSデータベースの開発・修正実績のあるはこにわガジェット(@hakoniwagadget)にお任せください。
ACCESSの新規開発、既存のACCESSの修正、ACCESSの操作レッスンなど様々なサービスをご提供しています。
ご興味のある方は下記のサービス一覧をご覧ください。

最後までお読みいただき、ありがとうございました。
