- ベストアンサー
アクセス2007でフォームフィルターをレポートにも使う方法
- アクセス2007のフォームフィルターを使って、特定のデータを表示する方法について解説します。
- フォームAとフォームBの関連付けについて説明し、フォームBでフィルターをかけた後にフォームAで該当データを表示する方法について詳しく説明します。
- しかし、フォームAを元にレポートCを表示する際には、フィルターが長くなるとエラーが発生することがあります。そのため、フォームの検索条件をレポートに反映する方法についても考える必要があります。
- みんなの回答 (6)
- 専門家の回答
質問者が選んだベストアンサー
フォームやレポートのレコードソースには、テーブルやクエリの他に SQL文を直接指定してやることもできます。 一例: http://okwave.jp/qa/q5784557.html ですので、フォームAのフィルタをDoCmd.OpenReportのWhere Conditionに指定する代わりに、レポートのレコードソースを 切り替えてしまうのが、現状からの大幅な改修は行わずに 目的の結果を得られる解決策ではないかと思います。 (No.5のお礼欄の対応をされた後では、ちょっと遅かったかも しれませんが(汗): 但し、『フォームAのクローンをつくって「選択」をTrue』という 操作で、もしもテーブルは元のフォームAと同じままだとすると、 ・クローンではなくフォームAそのもので処理してもいいか、 ・フォームだけでなくテーブルも別に用意しないとまずいか、 のような気がします(汗)) 以下、上記内容での改修案になります: 【現状】 DoCmd.OpenReport "レポートC", acViewPreview, WhereCondition:=フィルタ 【改修】 'フィルタは使用せずにレポートを開く DoCmd.OpenReport "レポートC", acViewPreview 'レポートのレコードソースを差し替え '(元のレコードソースが「クエリZ」だった場合) Reports!レポートC.RecordSource = "Select * From クエリZ Where " & フィルタ & ";" 上記の例は、元のコードが「フォームAで現在フィルタが 適用されているか」を判定していないので、それを踏襲 した形としました。 通常は、フィルタを解除(フォームのFilterOnプロパティを Falseに設定、或いはツールバー等で「フィルタの解除」 を指定)しても、フォームのFilterプロパティには、直前の 条件が記録されたままになりますので、フォームAの フィルタを解除した状態で、上記コードでレポートを開くと、 レポート側はフォームAと異なる結果を表示してしまいます。 これを避けるためには、フィルタ解除時に「Filter=""」の 代入を行うか、上記のコードを以下のようにして下さい: 【再改修】 Dim strRs As String '変数を宣言 DoCmd.OpenReport "レポートC", acViewPreview 'フォームAのフィルタ適用状況に応じてレコードソースを選択 Select Case Forms!フォームA.FilterOn Case True 'フォームAでフィルタ適用済 strRs = "Select * From クエリZ Where " & フィルタ & ";" Case Else 'フォームAでフィルタ解除済 strRs = "クエリZ" End Select '選択したレコードソースに差し替え Reports!レポートC.RecordSource = strRs ・・・以上です。
その他の回答 (5)
- NOBNNN
- ベストアンサー率50% (93/186)
#1 です。 一部、私の側でのミスがりましたので訂正いたします。 Access の使用では SQL の長さは 64,000文字、また vBA で記述できる文字列型については 文字列型 (String) 文字列には、可変長文字列と固定長文字列の 2 種類があります。 可変長文字列には、約 2GB (2^31) までの文字を格納することができます。 固定長文字列には、1 ~ 約 2GB (2^31) の文字を格納することができます。 となっています。 255文字としたのは デバッグウインドウで見るとそのように見えるので、勘違いしていました。 詳細は http://www.accessclub.jp/bbs2/0040/beginter12916.html いずれにせよ 長いSQL文は間違いの元になります。 できるだけ短くなるように工夫しましょう。 SQLの勉強については Google で「SQL入門」でさがせばたくさんあります。 できるだけ VBAでループ処理などは行わずSQLで解決できるように心がけてください。 できない場合はアクションクエリで 別テーブルを作成し、そのデータを入力データにすれば かなり効率的にできます。 これで 解決でしたら ベストアンサーボタン クローズしてください。 以上
お礼
長らくSQLを必要としない「ファイルメーカー」を使っていたので、勉強しなおし中です。 ほぼ完成、というところでレポートでひっかかってしまいました。 クエリは全部がデザインで組んでおり、いま更きつい所があります。 そこで、 テーブルAに「選択」フィールドを追加 フォームA、レポートCに「選択」を追加 レポートCを作る時 一旦全レコードの「選択」をfalseにUPDATE 抽出表示されたフォームAのクローンをつくって「選択」をTrue レポートCを開く時、Filterに「選択」=True、 データベースをまじめにやってる方には、ひっくり返るような回りくどさでしょうが、目的は達成しました。 ご親切なご回答、ありがとうございました。
- NOBNNN
- ベストアンサー率50% (93/186)
#3 のつづき もし、CreateQueryDef を使わないで行う場合は フォームBから抽出条件のみ フォームAにパラメータとして 渡してあげてください。 グローバル変数(モジュール内で定義) でできます。 例: モジュールを作成(Module1) Type T_抽出条件 お父さんの誕生日 As String お母さんの誕生日 As String 子供さん1の誕生日 As String 子供さん2の誕生日 As String 子供さん3の誕生日 As String End Type Public FM_A抽出パラメータ As T_抽出条件 フォームBからパラメータを設定 例: Privte Sub BB() With FM_A抽出パラメータ .お父さんの誕生日 = "1961/08/10" .お母さんの誕生日 = "1964/05/10" .子供さん1の誕生日 = "1990/05/10" .子供さん2の誕生日 = "1993/02/15" .子供さん3の誕生日 = "1996/09/24" End With End Sub フォームAでパラメータを受け取り クエリをレコードソースに指定。 例: Privte Sub aa() DIM Str_Where as string With FM_A抽出パラメータ Str_Where = Str_Where & " お父さんの誕生日 = '" & .お父さんの誕生日 & "' AND " & VBCRLF & _ Str_Where = Str_Where & " お母さんの誕生日 = '" & .お母さんの誕生日 & "' AND " & VBCRLF & _ Str_Where = Str_Where & " 子供さん1の誕生日 = '" & .子供さん1の誕生日 & "' AND " & VBCRLF & _ Str_Where = Str_Where & " 子供さん2の誕生日 = '" & .子供さん2の誕生日 & "' AND " & VBCRLF& _ Str_Where = Str_Where & " 子供さん3の誕生日 = '" & .子供さん3の誕生日 End with me.Recordsource = "SELECT TBL_A.* FROM クエリA基本 AS TBL_A " & VBCRLF & _ "Inner JOIN クエリB基本 AS TBL_B " & VBCRLF & _ "On TBL_A.家ID = TBL_B.家ID " & VBCRLF & _ " WHERE " & Str_Where & " " & VBCRLF & _ " Order BY TBL_A.家ID" End Sub
お礼
ご丁寧なご回答、ありがとうございます。 急きょ、ネット・参考書で勉強しました。 「NOBNNN」さんのご教示のとうりで実現できそうです。 だた、私の技量ではまだ少し無理みたいです。 勉強中に下記の記述を発見しました。 http://support.microsoft.com/kb/207615/ja wherecondition:=でなく、filtername:=フォームA.Filter これだと、文字数に制限がないみたいで、エラーも発生しませんでした。 但し「Loopで回してIDを得る」が効率がいいとは思えませんが、「RecordsetClone」で回すのは瞬時で終わるので苦になっていません。 次にデータベースを組む時は「NOBNNN」さんのご回答を頭に入れて作りたいと思います。 誠にありがとうございました。
補足
お礼の後の補足になってしまいました。申し訳ありません。 filtername:=フォームA.Filterでエラーにならずいけると思ったのですが、勘違いでした。 エラーにはならず、常に全表示になっていました。 filternameは「クエリの名前を文字列で指定」でした。 やはりクエリを勉強しないといけないみたいです。
- NOBNNN
- ベストアンサー率50% (93/186)
#1です。 >>例えばフォームBの検索条件に「お父さんの趣味=パソコン」とあってもフォームAに「お父さんの趣味」フィールドはないので。 こういう場合は、動的にクエリを作成してしまえばできます。 #1 でお答えしました CreateQueryDEF でクエリの中身を上書きしてしまう方法があります。 Access のクエリは 255文字が限界です。 だから 長い場合はあらかじある程度分割(別なクエリとして定義) するかデザイナでの入力はあきらめて 手動でSQLを書くことです・ デザイナのばあいは 中身が正規化されておらず かならずしも 最良のSQLではありません。 やたらと長く、短くはなりません。 デザイナに頼らず手動で行うには SQL が自分で書けることが条件です。 リレーショナル の記述法も知っている必要があります。 Access の場合は 長い SQL をひとつのクエリとして 保存し、それを再利用してひとつのクエリにできます。 初期の フォームA のクエリ を取り出して クエリを作製します。 初期の フォームB のクエリ を取り出して クエリを作製します。 例 フォームAデータ(家ID、氏名、住所、電話番号)これを クエリA基本 として作製してください。 フォームBデータ(家ID、族構成 お父さんの誕生日、お母さんの誕生日などなど)これを クエリB基本 として作製してください。 フォームB より条件が入力されたら 動的に抽出クエリ(求めたいフォームAデータ)を作成します。 フォームBから入力された条件(たとえば「お父さんの趣味=パソコン」)を作製したい場合。 SELECT TBL_A.* FROM クエリA基本 AS TBL_A Inner JOIN クエリB基本 AS TBL_B On TBL_A.家ID = TBL_B.家ID WHERE TBL_B.お父さんの趣味=パソコン Order BY TBL_A.家ID とすれば良いのです。 これを 先にのべたCreateQueryDef で 抽出クエリ(求めたいフォームAデータ)を作成します。 これを仮に 「フォームA抽出クエリ」とします。 あらかじめ フォームAおよびレポートCのレコードソースを 「フォームA抽出クエリ」としてください。 最初はつくられていないのでエラーになる場合は適当な内容でデザイナで「フォームA抽出クエリ」を 作製してください。 あとは フォームBから抽出条件が入力され、クエリ「フォームA抽出クエリ」を作製しましたので フォームAとレポートCを開くだけで 大丈夫です。 これでできるはずです。 ************ つづく *********
- NOBNNN
- ベストアンサー率50% (93/186)
#1 です。 気になる箇所があります 質問者さまの フォームBでフィルターをかけ、「家ID」を元にフォームAで該当するデータを表示させています。 ---------------------------------------------------- Set rs = Forms.フォームB.RecordsetClone Do Until rs.EOF フィルタ = フィルタ & "," & rs!家ID rs.MoveNext Loop ですが これってフォームBのデータが1000件あったら 全部対象てことですよね。 わざわざ ループさせなくても フォームBのデータから抽出できませんか? SELECT TBL_A.* FROM TBL家データ AS TBL_A Inner JOIN TBL家族内データ AS TBL_B On TBL_A.家ID = TBL_B.家ID Order BY TBL_A.家ID このクエリを フォームAおよびレポートCのレコードソースに指定すれば大丈夫では? もうすこし フォームBの絞込みの方法を教えてください。
お礼
補足の2です 気が付いたのですが、 >SQL文を取り出して、それにフォームBの検索条件を追加 はダメでした。 例えばフォームBの検索条件に「お父さんの趣味=パソコン」とあってもフォームAに「お父さんの趣味」フィールドはないので。 う~~ん
補足
早速のご回答ありがとうございます。 「Inner JOIN」は使ったこともなく、あわてて勉強してます。 フォームBで s = "SELECT * FROM フォームAのTB Inner JOIN フォームBのTB On フォームAのTB.家ID = フォームBのTB.家ID" Forms.フォームA.RecordSource = s としましたが、フォームAを見にいくと「#Name?」がたくさんあり、レコード数も「フォームBのTB」分です。 「Inner JOIN」の意味を体験した次第です。 初期のフォームAのレコードソースはフォームAクエリを指定しています。かなり複雑です。 (クエリはデザインで作っています) この初期のクエリのSQL文を取り出して、それにフォームBの検索条件を追加したらと思っていますが、方法が?です。 >フォームBの絞込みの方法 専用の検索フォームを作り絞込みをしています。 例えば、お父さんの誕生日、趣味とか・・・です。
- NOBNNN
- ベストアンサー率50% (93/186)
たぶん文字列の長さ255文字の限界を超えているのでは? フィルターは使わず。 レコードソースのほうで工夫してみてはどうでしょう たとえば フォ-ムからレポートを 一旦開き。 レコードソースを変更してしまえばできるのでは VBA で クエリを 動的に作製する方法があります。(CreateQueryDefメソッド ) 参考:http://www.accessclub.jp/dao/CreateQueryDef.html あらかじめ 雛形になるクエリを登録しておきます。 これを「クエリ雛形A」とします。 SELECT * FROM 住所録 「クエリ雛形A」 から 「クエリ_レポートデータ」を作成します。 SELECT * FROM クエリ雛形A Where 家ID IN (2,5,7) フォームAとレポートCのレコードソースのプロパティをこのクエリ名に変更します。 フォームBより、フォームAとレポートCのRecordsourceのプロパティ をあらかじめ「クエリ_レポートデータ」 とすればOKです。 VBAで変更する場合は下記のHPが参考になります。 参考:http://office.microsoft.com/ja-jp/access/HA012327891041.aspx 以上
お礼
お礼が大変遅くなり申し訳ありません。 「レコードソースの差し替え」了解しました。 一度チャレンジしたのですが、コードの書き方がわからず挫折してました。 クローンを使うのはスピードが全然違う為でした。 フォームでループさせるとスクロールもしてしまいます。 クローンだと一瞬で終わりますので。 ほぼ完成に近づいている状態で、レコードソースの差し替えについては今後勉強して使わせて頂きます。 ご回答、ありがとうございました。