Microsoft Access 掲示板

フォームのフィルターをクエリに反映可能?

19 コメント
views
4 フォロー

初級レベルのものです。部品発注ファイルを作成してまして、それの注残管理を注残ファーム(リスト形式)で確認しています。そのファームにはコンボボックスや文字列検索用テキストがあります。
コンボボックスには発注先、納期等が選択出来て更新後にフィルターかけてます。(文字列検索は抽出ボタンを作成)
フィルターコードは下記でしております。
Dim strFilter As String
   If Not IsNull(Me.[cb発注先選択].Value) Then
     strFilter = strFilter & " AND 発注先ID =" & Me.[cb発注先選択]
   End If
   If Not IsNull(Me.[cb納期検索1].Value) Then
      If IsNull(Me.[cb納期検索2].Value) Then
          strFilter = strFilter & " AND 納期=#" & Me.[cb納期検索1] & "#"
      ElseIf Not IsNull(Me.[cb納期検索2].Value) Then
         strFilter = strFilter & " AND 納期 Between #" & Me.[cb納期検索1] & "# And #" & Me.[cb納期検索2] & "#"
      End If
   End If

   If Not IsNull(Me.[cb直送先選択].Value) Then
      strFilter = strFilter & " AND 直送先 Like '" & Me.[cb直送先選択] & "'"
   End If

   If Not IsNull(Me.[cb検索値].Value) Then
      strFilter = strFilter & " AND 検索用文字 Like '" & Me.[cb検索値] & "'"
   End If
   strFilter = mid(strFilter, 6) '先頭の" AND "を削除
   Me.Filter = strFilter

   If Not IsNull(strFilter) Then
      Me.FilterOn = True
   Else
      Me.FilterOn = False
   End If
ここまでは動作できてます。このフォームにフィルターかかった条件をクエリに反映できないかと思ってます。そしてそれをExcelにエクスポートさせたいのです。
今までコンボボックスに値あるなしの組合せで条件で各テーブル作成クエリを実行しそのテーブルをエクスポートさせていましたた。条件が多いとテーブル作成クエリが増えてしまい困ってまして(条件も複雑になるので)、フォームのフィルター条件を動的にテーブル作成クエリが出来ないかなと思い投稿しました。

tom
作成: 2024/05/07 (火) 16:04:28
通報 ...
1
hiroton 2024/05/07 (火) 17:00:55 d915e@f966d

いろいろ方法はあると思いますが、とりあえずこれ

https://www.google.com/search?q=access フォーム フィルター excel出力


フォームのフィルターをクエリに反映可能?

できる、できないで言えば出来ます

ACCESS クエリの抽出条件でフォームを参照させる方法(VBAコード公開)(たすけてACCESS さん)
※ページタイトルそのまま記載していますが、リンク先でVBAは使われていません

全てのフィルターを必ず設定するならフォームのコントロールを参照する式を設定すれば問題ありません。そうでない場合は、「固定で組み込んでしまった抽出処理をどのように回避するか?」という複雑な問題が発生します

Excelへの出力が目的なら
・フォームで抽出を行い、フォームのレコードを直接出力する(VBA)
・複雑なクエリを組んで抽出条件の有無にどうにか対応する
・毎回クエリそのものを作り直す(VBA)

どれかの手法になると思います

VBAのコードはコピペして使えるものも検索で見つかります。用法に合うならそれが一番簡単だと思います。手直しが必要な場合は、規模・能力によってVBAまたは、複雑なクエリどちらかでしょう
クエリそのものを作り直すのは「できる」というだけの話でそこまで大事にすることではないでしょう

2
hatena 2024/05/07 (火) 18:14:35 修正

その要件なら、ダミーのクエリを作成しておいて、そのSQLを書き換えてエクスポートするという方法が簡単ではないでしょうか。

下記で、フィールドを選択してエクスポートするという方法を紹介しています。
それの応用でできるでしょう。

テーブルのフィールドを指定してエクスポート - hatena chips

Q_Dummy という名前のクエリを作成しておきます。上書きしますので中身はなんでもいいです。

Private Sub cmdExport_Click()
    Dim strFileName As String
    strFileName = "C:\Test\Test.xlsx"
    Dim strSQL As String   
    strSQL = "SELECT * FROM テーブル名 Where " & Me.Filter & ";" 
    CurrentDb.QueryDefs("Q_Dummy").SQL = strSQL
    DoCmd.TransferSpreadsheet acExport, , "Q_Dummy", strFileName, True
End Sub
3

hirotonさん・hatenaさんありがとうございます。大変参考になります。
クエリもフォームやレポート同様にMe.Filter条件が可能とは知りませんでした。
いつもクエリばかりでSQL記述した事がありませんが、内容を勉強してある程度理解したら試してみます。
それで分からない事がでてきたらまた投稿致しますので宜しくお願いします。

4

プロパティやコマンド、関数などはヘルプや公式ドキュメントで確認するようにするとヒントがいろいろ見つかります。

Form.Filter プロパティ (Access) | Microsoft Learn

注釈

Filter プロパティの設定値は、WHERE 句から WHERE を省いた文字列式です。

5
hiroton 2024/05/08 (水) 09:54:06 d241b@f966d

ACCESSのクエリは実体はテキストデータです
クエリのビューの切り替えでSQLビューを表示すればSQL記述を確認できます
SQLビューであればテキストのコピペができるのでいろいろ捗ります

クエリデザインで作成したものをSQL表示してみる、または、SQLコピペしたものをクエリデザインで確認するなどすると理解が進むと思います(クエリを保存すると自動整形される/されてしまう点には注意が必要です)


hatenaさん記述のMe.Filterは抽出条件入力用フォームのフィルター(文字列)をフォームのモジュール内から参照しています
流れ的には、フォームのフィルター文字列を使って新たなSQL構文を作成し、それを基に新たなクエリを作成しています。Excelへのエクスポートの前に毎回クエリを作り直すイメージですね
クエリそのものがMe.Filterを扱えるわけではありません


Q_Dummyに関する注意点としては、ぱっと見それが何者か分からないということです。また、条件抽出用のフォームと依存関係にあるのに、ナビゲーションウインドウから直接開けてしまう(クエリを開いた場合のデータの正しさが保証されていない)という問題があります

ACCESSで作るシステムにどこまで要求するか?仕様と開発コストからどの程度まで妥協するかというところですが、開発(プログラミング)の原則から見ると推奨されない構造を取ることになります

6

Form.Filter プロパティ (Access) | Microsoft Learn
補足
フォームのFilterプロパティに設定する条件式は、クエリのWHERE句以降に設定する条件式と同じものです。よって、クエリのWHERE句にFilterプロパティの値を設定することはできます。


今回のようにエクスポートするときに、
一時的に使うクエリを事前に作成しておいて、エクスポート時にそれを書き換えて利用する、
という運用は特に問題ないと私は考えてきます。

名前付けに関してはQ_Dummyでは何者か分からないというのなら、分かる名前にすればいいでしょう。「エクセルエクスポート用一時クエリ」とか。

ユーザーに触られるのが不安なら、「隠しオブジェクト」にしておけばナビゲーションウインドウでは非表示にできます。

別の方法としては、エクスポートするたびにクエリを新規作成(CreateQueryDef)して、エクスポート後削除するということも考えられますが、
Accessでオブジェクトの作成、削除を繰り返すことは、ファイルサイズが増えたり、破損の危険性が高くなるので、私としては避けたいと思っています。

7
名前なし 2024/05/09 (木) 10:59:59 b4927@fc3ed

Dim strFilter As String

・String 型の変数の初期値は Null ではなく空文字列である。

・String 型の変数に Null を代入することは出来ない。

If Not IsNull(strFilter) Then

・したがって、上記の条件式の結果は常に True となる。

   If strFilter <> "" Then
      Me.FilterOn = True
   Else
      Me.FilterOn = False
   End If

・フォームの Filter プロパティに何らかの Where 条件が設定されていても、
 FilterOn プロパティが False である状態であればフォームフィルターは
 適用されず、レコードソースの全てのレコードが表示される。

Dim strSQL As String
strSQL = "SELECT * FROM テーブル名 Where " & Me.Filter & ";"
CurrentDb.QueryDefs("Q_Dummy").SQL = strSQL

・[ホーム]タブ上の[フォームの実行]ボタンや、フォームの移動ボタン上の
 [フィルター適用/フィルター処理なし]ボタンをユーザーがクリックすることによって
 フォームフィルターが解除され得ることを想定するのであれば、Filter プロパティの
 値が空文字列ではなく、かつ FilterOn プロパティの値が True である場合のみ、
 SQL に WHERE 句を付加するようにすることが望ましい。
 (でないと、フォーム上に全てのレコードが表示されているのに対し、
 クエリ側ではレコードの抽出が行われているという矛盾した状態となり得る)

Dim strSQL As String   
strSQL = "SELECT * FROM [テーブル名]"
If Me.Filter <> "" And Me.FilterOn = True Then
    strSQL = strSQL & " WHERE " & Me.Filter
End If
strSQL = strSQL & ";"
CurrentDb.QueryDefs("Q_Dummy").SQL = strSQL
8

ハンドルネームを入力し忘れました、失礼。

9

[ホーム]タブ上の[フォームの実行]ボタン

あと[フィルターの実行]ボタンでした。

10

色々とありがとうございます。奥の深いことがあり、勉強になります。
作成してますファイルは私を含めた社内の担当しか使いませんので Q_Dummy は非表示にする必要はありません。
対象フォームは何かフィルターをしなければ対象項目がリスト表示されない形です(例えば発注先を選択等)
まず細かい処理はせずにhatenaさんのコードを使い試してみました。問題なく動作しました(フォームと同じフィルターでエクスポート出来ています)こんなに簡潔に出来て感激です。
無知ついでにお聞きしたいのですが(全然理解出来ておらず恥ずかしい限りですが)、今回はSQLだけを使い実行出来ましたが、DAOでレコードセットにして処理する方法もあるのでしょうか?
レコードセットは漠然としたイメージしかなく、どういう時に使うのかなと思ってましたので。ネットの情報見てもSQL、DAOの関係がつかめないもので(初級レベルの事ですみません)

11
hiroton 2024/05/09 (木) 14:09:50 7f972@f966d

どの手法をとるにしても細かいところは出てくるものです。躓かない限りは気づかず済むことも多いのでACCESSで済むうちは「できればOK」みたいなところもありますね
(逆に躓くと、細かいところに気づくまで深みにハマるわけですが)

DAOでレコードセットにして処理する方法もあるのでしょうか?

>> 2でGoogle検索のURLを出していますが、その検索結果でやり方を公開しているサイトが見つかります

12

DoCmd.TransferSpreadsheet というのはAccessの機能、
リボンメニューのエクスポートあるいは右クリックのエクスポートの機能を、VBAから実行しています。

SQLというのは、テーブルデータの抽出、集計、並べ替え、更新・・・・・などのデータ操作をするためのデータベース用言語、
それを視覚的に扱えるようにしたのがAccessのクエリ。SQLビューにすればSQLコードをみることができます。

DAOというのは、データベースのデータ操作を、VBAなどのプログラミング言語から操作できるようにしたライブラリー。
Accessのテーブルだけでなく、他のデータベースのデータや、CSVやエクセルのデータもデータベースとして扱えます。

AccessのテーブルデータをDAOでエクセルに出力するサンプルコードは、
ちょっと古いですが、下記にあります。

access技メモ(2003-2007)詳細

こちらのコードでは、OpenRecordset でテーブルを開いていますが、SQLで開くこともできますので、

Set rs = db.OpenRecordset("SELECT * FROM テーブル名 Where " & Me.Filter & ";")

とすればフォームと同じレコードセットを取得できます。

エクセルはCopyFromRecordsetというレコードセットのデータを直接シート状に出力するメソッドがありますので、
それを利用して貼り付けます。

13

hirotonさん hatenaさん ありがとうございました。こつこつ勉強してステップアップ出来たらいいと思ってます。
今回の処理も今までクエリを沢山作ってたで画期的でした。
どういうときにDAOが必要かわかっていませんが(SQLだけで出来る事多いので)、色々と勉強します。
本当にありがとうございました。

14
hiroton 2024/05/09 (木) 17:58:08 7f972@f966d

どういうときにDAOが必要か

大抵はクエリによる代替手法がありますね。データベースというのはそういうものなので

たとえば、今回の例でなら、既に回答した部分ですが、機能が一か所にまとめられるというのはそれだけでメリットになります(データベース的なメリットではないですが)

また、出力先のExcelの書式が決まっていて特定の位置に出力したいなんていうときには、「貼り付け先を指定できる」や、それこそ「レコード、フィールドごとに出力先を指定できる」というような細かい作業ができます(これも、データベースではないモノを相手にする場合という視点ですね)

そのほか、よくありそうなのは、ACCESSで連番を使いたいなんて場合があります
グループ毎連番を自動入力する関数(hatena chipsさん)
クエリだけでも実現可能ですが、データの量によってはより高速な手法が望まれることがあります
基本的にACCESSはデータを縦方向に処理するのを苦手とするので、「前後のレコードの関係性から何かの処理をする」様な場合はDAOのほうが向いていることがあります

あとは、データ処理を伴いつつ複数のレコードを処理する必要が出る場合なんかも、都度クエリを実行するよりはDAOの処理を組んだほうが良くなったりします
例えば、生産管理を考えたとき、在庫管理をしつつ、不足分は発注するのような処理は状態によるパターンが複数あり、クエリだけで処理をするのは難しくなったりします

15

hirotonさん度々ありがとうございます。ネットでDAO処理で”レコードセット出来ればこっちのもの”というコメントを見た事がありますが、それがまず重要なのですね?
あと『クエリを保存すると自動整形される/されてしまう点には注意が必要です』とありましたが自動整形されると何が問題になることがあるのでしょうか?

16
hiroton 2024/05/10 (金) 11:08:55 f2bfb@f966d

hirotonさん度々ありがとうございます。ネットでDAO処理で”レコードセット出来ればこっちのもの”というコメントを見た事がありますが、それがまず重要なのですね?

「レコードセット出来れば」がちょっとどういったことを指しているのか分からないので、「それ」が重要かどうかは何とも言えないですね
まぁ、クエリだけではどうにもうまくいかない、手間(時間)がかかるケースに出くわしたらDAOを思い出したらいいでしょう

『クエリを保存すると自動整形される/されてしまう点には注意が必要です』とありましたが自動整形されると何が問題になることがあるのでしょうか?

とくにはないです。SQLビューで開くと汚いとか、SQLビューでしか編集できないような複雑なクエリを修正しようとすると汚くてまず整形からやらないとやってられないとか、VBAで動的なSQLを生成しようと思ってベースのSQL文を作るために使うとまぁまぁ修正作業が必要とかです

あとは、稀に、手書きのほうが圧倒的に簡素な記述がデザインビューで作ると、ちょっとこれは作るのに苦労するなって形になってしまうことがないわけでもありません
こういうのはSQLを難しいと思ってしまう原因になったりもするので、SQLビューの記述は絶対ではないというのは頭に置いておくといいかもしれません

17

hirotonさん ありがとうございました。参考になります。
SQLビューは確かに参照が多かったり抽出条件が多いと非常に見にくくなりますね。
初歩的な事に何度もコメント頂きありがとうございました。

18

あと、skさんのコメントを注意して読めていませんでした。すみませんでした。落ち着いて読むと、すごく大事な指摘でした。ありがとうございます。変数の初期値は全く認識ありませんでした。フォーム上のテキストボックス等のコントロール値の入力ありなしではないので If strFilter <> "" Thenでないとダメなのですね。勉強になりました。

19

先日 strSQL = strSQL & " WHERE " & Me.Filter・・・を教えて頂いた者です。
関連のフォームでSQL実行させたい事があるのですが分からないことがでてきました。先日の応用が出来なくて、もどかしいです・・・。やりたい事は部品現品は入荷済みで納品書が未達の分を抽出するフォームを作成しています。そのフォームには仕入先、入荷日、直送先等の抽出条件コンボボックスがあり前回同様 strFilter = mid(strFilter, 6) で抽出出来ています。レコードにはチェックボックスがありチェックYesにしたものだけに後日届いた納品書の日付をフォーム上のテキストボックスから代入して一括入力させたいのです(レコードに1件づつ直接日付入力も可能)。レコード1件づつチェックYesにする事も可能ですがFilter抽出した分にその一括処理をする更新クエリをしているのですが、現状抽出条件毎にクエリを複数作成しています。それを集約させたいのでWHERE " & Me.Filterでしようと思っていたのですが、いざ記述しようとするとどうやって?となってしまったのです。更新クエリのSQLビューは下記です。
UPDATE T納品書まだ分 SET T納品書まだ分.チェック1 = Yes
WHERE (((T納品書まだ分.納品書入り日) Is Null) AND ((T納品書まだ分.発注先ID)=[Forms]![F納品書未達処理]![cb発注先選択]));
この[cb発注先選択]部を Me.Filter にしたいのです。T納品書まだ分.納品書入り日) Is Nullの後にどうコードをつないだらいいのかが分からなくて。一括でチェックYes処理はフォーム上のボタンにDoCmd.OpenQuery・・・で処理させてましたがSQL勉強中なのでそれで処理させようと思ったのですがやり方が分からないのが現状です。
最終的にはチェックYes分に納品書日付を一括代入するボタンもSQL(更新)で処理させたいです。
またこの更新SQLの実行もVBAでどう実行させたらいいのかも分からなくて。
応用力がなく情けないのですが、アドバイスを宜しくお願いします。