減算補正:IIf([選択1のID]=9,[減算値],0)
月補正:IIf([選択1のID]=9, Switch([A]=0 And [B]>0, 1, [A]>0 And [B]=0, -1, True, 0), IIf([X]>[Y] Or [選択2のID]=1 Or [選択2のID]=5, 1, 0))
//Switch([1になる条件], 1, [-1になる条件], -1, True, 0)の形にする
月補正:Switch([選択1のID]=9 And [A]=0 And [B]>0 Or [選択1のID]<>9 And ([X]>[Y] Or [選択2のID]=1 Or [選択2のID]=5), 1, [選択1のID]=9 And [A]>0 And [B]=0, -1, True, 0)
IIf([選択1のID]=9,IIf([A]=0 And [B]>0,DateSerial([年],[月]-[減算値]+1,[実日]),IIf([A]>0 And [B]=0,DateSerial([年],[月]-[減算値]-1,[実日]),DateSerial([年],[月]-[減算値],[実日]))),IIf([X]>[Y],DateSerial([年],[月]+1,[実日]),IIf([選択2のID]=1,DateSerial([年],[月]+1,[実日]),IIf([選択2のID]=5,DateSerial([年],[月]+1,[実日]),DateSerial([年],[月],[実日])))))
アドバイスありがとうございました。選択1のIDが9の時、[A]=0の真、偽+選択2のID以降の条件式を記述した時は生成できてたのです。選択1のIDが9の時に更に分岐(A]=0 And [B]>0、[A]>0 And [B]=0、それ以外)を付け加えようと思い質問のコードにすると生成できないのです。
SELECT Format([売上日],"yyyymm") AS 年月
, Sum(IIf(Day([売上日])<=15,[金額],0)) AS 月前半金額
, Sum(IIf(Day([売上日])<=15,0,[金額])) AS 月後半金額
FROM 売上テーブル
GROUP BY Format([売上日],"yyyymm");
SELECT Format([売上日],"yyyymm") AS 年月
, IIf(Day([売上日])<=15,0,1) AS 月内区分
, Sum([金額]) AS 金額の合計
FROM 売上テーブル
GROUP BY Format([売上日],"yyyymm"), IIf(Day([売上日])<=15,0,1);
Private Sub btn_担当者別プレビュー表示_Click()
If Trim(Nz(Me!cmb_担当者名, "")) = "" Then '担当者を選択していない場合
Exit Sub
End If
DoCmd.Close acReport, "R_出力用レポート" '連続してプレビュー表示を押した時用
DoCmd.OpenReport "R_出力用レポート", acViewPreview,,"担当者='" & Me!cmb_担当者名 & "'"
DoCmd.RunCommand acCmdPreviewOnePage
End Sub
Private Sub btn_担当者別プレビュー表示_Click()
If Trim(Nz(cmb_担当者名, "")) = "" Then '担当者を選択していない場合
Exit Sub
End If
DoCmd.Close acReport, "R_出力用レポート" '連続してプレビュー表示を押した時用
DoCmd.OpenReport "R_出力用レポート", acViewPreview,,担当者=[Forms]![F_担当者別出力]![cmb_担当者名]
DoCmd.RunCommand acCmdPreviewOnePage
End Sub
Dim strFilter As String
Dim 開始年 As Long
Dim 開始月 As Long
Dim 終了年 As Long
Dim 終了月 As Long
'データチェック
If IsNull(Me!cmb期間開始) Or IsNull(Me!cmb期間終了) Then Exit Sub
開始年 = Me!cmb期間開始.Column(1)
開始月 = Me!cmb期間開始.Column(2)
終了年 = Me!cmb期間終了.Column(1)
終了月 = Me!cmb期間終了.Column(2)
'範囲開始
strFilter = strFilter & " AND NOT 年<" & 開始年
strFilter = strFilter & " AND NOT (年=" & 開始年 & " AND 月<" & 開始月 & ")"
'範囲終了
strFilter = strFilter & " AND NOT (年=" & 終了年 & " AND 月>" & 終了月 & ")"
strFilter = strFilter & " AND NOT 年>" & 終了年
Me.Filter = Mid(strFilter, 6)
「任意のフィルタを設けたい」時にコードが簡素に記述できます
フィルタを複数項目で連続してかける場合、それぞれの条件を AND でつなぎますが、「項目が任意」の場合、既にフィルタがあるかどうか( AND をつけるかどうか)をチェックするのが煩わしくなってきます
なので、フィルタをつける場合はとりあえず AND をつける、最後に先頭の AND を除いた文字を取り出すという手順にして簡素化しています
hatenaさん丁寧な解説ありがとうございます。SQL、DAOで直接した事はなくチャレンジしてみたいと思いますが、因みに手始めに追加クエリをメイン用、サブ用を用意してクエリ実行でも可能でしょうか?(すみません)
それで試しましたが型変換エラーが起きました。
置換用のテーブル・T_出力のテーブルはいずれのフィールドも短いテキスト型です。
また、総レコード4件、住所1と住所2だけを横並びにしてOr条件で更新をかけてみたところ、
12件の型変換エラーが出ました。
住所1はあるが住所2はない(住所2がNull?)レコードが実行前に12件あったので
Nullを短いテキスト型に更新しようとしてるのでは?と考えています
それぞれのフィールドの同じように設定すればいいのでは。
ただし、抽出条件は1行ずつずらして設定します。(OR条件になる)
あるいは、抽出条件をなしにてしもいいでしょう。
Replace([T_出力].[氏名],"??","?")で解決しました。
ある程度分けてクエリを作成してメンテナンスしやすしたいので、別クエリで作成しようと考えています。
わざわざ後から更新クエリ走らせずに、StrConvしてるところでReplaceもしてしまえばいいんじゃない?
Replace 関数
下記はDAOを使って新規追加する場合のコード例です。
こちらの方が分かりやすいかもしれません。
可能です。
下記はVBAで追加クエリのSQLを実行する場合のコード例です。
詳細が不明なので、テーブル名、フィールド名、コントロール名は適当ですので雰囲気だけつかんで、あとは状況に合わせて改修してください。
そのクエリをレコードソースにしたフォームを新たに作り、望みの表の見た目にしてメインのフォームにサブフォームとして設置したらそれっぽいと思います
仕様がよく分からないんでクエリの回答してますけど、フォーム上に2つの合計値が表示されればいいだけならテキストボックスで計算させてもいいんじゃないですかね?
DSUM 関数
hatenaさん ありがとうございます。2段階でも全然OKなのですが、それはコマンドボタン1つで可能でしょうか?
ありがとうございます。
こちらのクエリの結果を年月を指定したフォーム上に表1のような形で表示させたいと考えているのですがどのように行えばよろしいのでしょうか。
メインに追加してから、サブに追加という2段階ではだめなんですか。
メインのIDがオートナンバー型でインクリメントなら、最大値が新規IDということになります。
hiroton様 回答ありがとうございました。なるほどです! 一旦条件分岐での補正フィールドを作りそれを参照させると大分すっきりしますね。IIFとSwitchを組み合わせることもできるのですね。論理的なアイデアは素晴らしいです。
同じ結果になるものは一つにまとめるといいですね。短時間でこんなすごいアイデアがでてくるのは、本当にすごいです。
ありがとうございました。
最終的にすべて
DateSerial(y, m, d)
の計算をしているので、DateSerial
を主に置くとすっきりすると思います必要な要素をまとめて式にすると、次のように表現できそうです
「減算補正」はマイナス計算しかないので式自体マイナスにしていますが、「月補正」はプラスマイナスあるのでプラスで作成しておきます
それぞれの補正の計算は
のような形で作れます
これを上の式に当てはめてもいいですし、そのまま別フィールドにしてもいいでしょう
「減算補正」は簡単ですが、「月補正」は複雑ですね。求め方はこれに限りません
例えば、最終結果(1,-1,0)のグループを作る形で
のようにしてもいいと思います
関数のネストが減るメリットはあるものの、やりたいことを直感的に表現できているか?とはちょっと離れているかな?という印象です
Switch関数は条件が真(
True
)になったときの値を返す関数なので、条件にTrue
を指定すれば、そこで必ず値を返すことになります。「それ以前の条件で真が無ければこの値を返す」という、IIf関数における「偽のとき」の値を設定できます条件式では様々な比較演算子を使うことができます
このようないずれかに当てはまれば真というのは
Or
で表現できますOr
比較はAnd
比較と同じくらい使うので覚えましょう蛇足
こう複雑な計算が必要になると、なんかこう、元のテーブル構造が怪しそうですね
アドバイスありがとうございました!そのコードで上手くいきました!途中の ) が不足で最後の )が多かったのですね。分岐が多く混乱してました。IIFの )はコツがあるのでしょうか? Switch関数の方がすっきりするのですが、この関数はIIFの様に偽の場合はないのですよね? 初心者で見よう見まねでしているもので。
IIf([選択1のID]=9,IIf([A]=0 And [B]>0,DateSerial([年],[月]-[減算値]+1,[実日]),IIf([A]>0 And [B]=0,DateSerial([年],[月]-[減算値]-1,[実日]),DateSerial([年],[月]-[減算値],[実日]))),IIf([X]>[Y],DateSerial([年],[月]+1,[実日]),IIf([選択2のID]=1,DateSerial([年],[月]+1,[実日]),IIf([選択2のID]=5,DateSerial([年],[月]+1,[実日]),DateSerial([年],[月],[実日])))))
こちらでどうでしょうか。構文エラーは出ないと思います。
アドバイスありがとうございました。選択1のIDが9の時、[A]=0の真、偽+選択2のID以降の条件式を記述した時は生成できてたのです。選択1のIDが9の時に更に分岐(A]=0 And [B]>0、[A]>0 And [B]=0、それ以外)を付け加えようと思い質問のコードにすると生成できないのです。
ネストが深くなり過ぎですね。
可読性やメンテナンス性の面でかなり難ありのコードになっています。
階層毎に、意図する処理が行われているかテストしながら記述しましょう。
ありがとうございます。
無事に置換テーブルに全角スペース・半角スペースを入力することができました。
AccessのUIで入力すると、Accessが気を利かせて(よけいなお世話で)、半角空白のみのフィールドはNullに変換します。
UIを使わずに追加(更新)クエリやVBAで入力すると半角空白でも保存できます。
追加クエリの例
ちょっと理解しきれてないけど、月の前半と後半、金額を振り分けて合計したらいいんじゃない?
レコードが分かれていたほうが都合がいいならレコードごとに前半、後半のフラグを立ててそれでグループ化するとか
抽出条件/フィルタの設定をクエリに入れるか、VBAでやるかはお好みで
hirotonさん ありがとうございました。消去方的な感じだったのですね。図まで用意して頂き大変分かり易かったです。お手数かけました。逆の発想が中々出来ず、自分自身がもどかしいところです。本当に色々とありがとうございました。
お礼が抜けておりました、本当にありがとうございました🥰
はい 変数でも直接でも問題ないと思います。
WHERE句?を正しく指定出来たら、きちんと各担当者のレポートだけが表示されるようになり、何故か実行時エラーの問題も解決しました!!クエリもレポートも減ってスッキリしました
コピペされてないようなので何とも言えないですが
として、コードを試してください
strWhereCondition
はTrue
となって、つまり全レコードを対象にするフィルタ(無意味)をかけてるんじゃないですかねなので、きちんとWHERE句の文字列になるように渡しましょう
あと、自身のフォームのコントロール(コンボボックス)を指定するときは
Me
キーワードを使用するといろいろと捗りますついでに、「担当者名」って文字列ですよね?
Private Sub btn_担当者別プレビュー表示_Click()
If Trim(Nz(Me!cmb_担当者名, "")) = "" Then '担当者を選択していない場合
Exit Sub
End If
DoCmd.Close acReport, "R_出力用レポート" '連続してプレビュー表示を押した時用
DoCmd.OpenReport "R_出力用レポート", acViewPreview,,"担当者='" & Me!cmb_担当者名 & "'"
DoCmd.RunCommand acCmdPreviewOnePage
End Sub
こっちはだいぶ問題が異なりそうなのでひとまず無回答で
つまり操作するルート(フォーム別等)によって、クエリの中身を置き換えるということですよね。
クエリ部分の文字列を変数で設定すればいいのではないでしょうか。
これで試してみましたが、フォームのコンボボックスから担当者を1人選ぶとその担当者以外のレポートも入ってしまっています。
また、標準モジュールにAutoFontSize関数を記述しているのですが、他の担当者を選択してプレビューを表示しようとすると、実行時エラー2185が出ます。このサイトと同じ状況です。
既に説明しているところの繰り返しになりますが
を実現するためにはクエリ(SQL構文)で
を実行します
ACCESSの仕様として、フォーム上でフォームのレコードソースからさらにデータを抽出する場合、フィルタプロパティにSQLのWHERE句と同じ記述方法を指定することができます
フォームのプロパティはVBAから操作することができ、フィルタプロパティは
Me.Filter
で参照しとすれば、フィルタをかけることができます
なので、
NOT
はSQL構文で使うところのNOT
です。上記のWHERE
をNOT
が掛かる部分ごとに分けると次のようになりますNOT
は「否定」を表すので、「指定内容以外はOK」となります。つまり、「ここはダメ」を指定します。必要なだけ「ここはダメ」を重ねる(AND
で繋げる)と欲しい部分が残りますまぁ、SQLの記述方法はいろいろあるので、ここの方法は一つではありません。
NOT
を使わずに記述することもできるでしょう。そういうのも経験ですねApplication.CurrentDb.QueryDefs("クエリ名").SQL = "SELECT FROM ***"
クエリの中身書き換えるとか
hirotonさん ありがとうございました。コードの事で strFilter & " AND NOT 年< 等でこのNOTはどういう意味になるのでしょうか? 理解力がなくてすみません。
ただ、元の質問をみると、「年」と「月」でコンボボックスを分けるのはあんまり望んでなさそうですよね
テーブル構造で「年」「月」分けた場合でも、コンボボックスのSQLでユーザー選択用に「年月」表記にして、コンボボックスは一つにしてもいいと思います
これで、コンボボックスの列数プロパティを「3」にすると、フォーム上の表記は「yyyy/mm」形式で、コードでは2列目、3列目を参照して「年」「月」のデータを直接使えるようになります
範囲の開始用を「cmb期間開始」、終了用を「cmb期間終了」とすることにして
コンボボックスの列指定まで入れるとコードが見にくくなるので、それらもそれぞれ変数に置くようにすると、SQL構築部分もすっきりしていいんじゃないかと思います
まぁ、どこまで細かく記述するか?は慣れと好みでいいところではあります
上のSQL構文を元にたろうさんのデータでSQLビューでクエリを作成したあと、保存したクエリを開きなおしてみてください。デザインビューで表現する方法が表示されると思いますが、hirotonはサッと作れるようなデザインではなかったですね
あとは、任意項目でフィルタを掛けたい場合なんかはVBAで作ったほうが楽なので、やっぱりSQLそのものを覚えていくと出来ることが増えるでしょう
コードでフィルタを作る場合も、SQL構文のWHERE部分を作ればいいだけなので、上記の形でいいならそのまま、数字部分をコントロール参照に変えるだけですよ
1行で記述してもいいと思いますが、分割しながら記述するなら
hirotonさん ありがとうございます。クエリデザインよりもSQLで記述した方が簡単な事も多いのですね。SQL記述を勉強した方が今後いいですね。(今まで直接記述したことがないので)
何度もすみません、この関連で最後に1つだけ教えて欲しいのですが、年と月だけを使って(数字扱いで)範囲指定するにはどういうコードになりますでしょうか?(Filter)
例えば年の数値データが2023、2024とあり月の数値データが1~12があります。一つの年月なら下記で出来るのですが
strFilter1 = "年 =" & Me.[コンボ年1]
strFilter2 = "月 =" & Me.[コンボ月1]
Me.Filter = strFilter1 & " AND " & strFilter2
範囲となるとコードがややこしいもので。(コンボ年2、コンボ月2を範囲最後として)
前提として、「日」までデータに含むなら「年月日」で一つのデータにしたほうがいいです
不要部分を丁寧に除外してくとわかりやすいんじゃないかと思います
SQL直接記述するとなんてことないんですけど、クエリデザインで作ろうとしたらなかなか大変ですねこれ
レポートを開く時に追加の抽出条件を加えればいいですよ
DoCmd.OpenReport メソッド (Access)
※「同時に複数のレポートを画面に表示したい」となるとこれだけではうまくいかないですが
hirotonさん すごく分かり易い説明ありがとうございました。自分では全くでてこないアイデアです。
日付を年・月・日に分けた場合(クエリでYear関数等で)、単独日付のフィルター式はANDで結合させて記述できるのですが範囲指定となるとどうもややこしくて、避けています。例えば2023・10・01 から 2024・01・31 は年月日が分割されたデータを使ってどうやってコード記述したら出来ますでしょうか?
一応の確認ですが、プログラミングにおける(特にここの)「
=
」は代入を表していて「左の変数に右の計算結果を代入する」という動作をしますなので、
の形は、「元の変数の後ろに文字を追加する」記述になります
Me.Filter = Mid(strFilter,6)
についてMid 関数
「任意のフィルタを設けたい」時にコードが簡素に記述できます
フィルタを複数項目で連続してかける場合、それぞれの条件を
AND
でつなぎますが、「項目が任意」の場合、既にフィルタがあるかどうか(AND
をつけるかどうか)をチェックするのが煩わしくなってきますなので、フィルタをつける場合はとりあえず
AND
をつける、最後に先頭のAND
を除いた文字を取り出すという手順にして簡素化していますもう一つ、改修を見越してというのもあります
「フィルタ項目を追加したい」となった場合、上記のような問題を考える必要がでるので、もともと対応済みの記述にしておけば、「元のコードに手を付けず、追加したい項目の処理を追加するだけ」で済みます
「年月」データの持ち方について
パソコンは基本的に文字列よりも数値のほうが楽に扱えます。日付のデータも、表示上は書式設定にて「yyyy/mm/dd」形式で表示し、内部的には数値データとして持つ場合が多いです(シリアル値と呼ばれたりします)
この場合、関連する日付を数値計算で算出したり、日付計算用の関数が使えたりするというメリットが受けられます
ただし、必ず「日」のデータまで必要なので「年月」でいい場合には注意が必要です。「日」まで気にして範囲指定しないと漏れが出たりします(そのための計算で複雑化したりします)
現状であろう「年/月」の文字列で保存するとか、「yyyymm」を数値として保存するとかありますが、桁数(月の十の位の0詰め)に気を付ける必要があるとか出ますし、究極的には「年」「月」でフィールドを分けて保存するのが一番安全なのかなと思っています
(連絡が遅くなりました)hirotonさん hatenaさんありがとうございました。
hirotonさんのアドバイス通りNull時の条件にNz使い、年月部に関係ない文字列変換をやめたたら挙動がなくなり安定してFilterが出来てます。Nz関数はよくつかいますがクエリ条件に使うアイデアは全く思いもつきませんでした。
ひとつだけ理解できないのですが(知識不足で)最初の回答の下記です。
Dim strFilter As String
'//範囲開始フィルタ
strFilter = strFilter & " AND 年月>= '" & Me.[コンボ年月1] & "'"
'//範囲終了フィルタ
strFilter = strFilter & " AND 年月<= '" & Me.[コンボ年月2] & "'"
Me.Filter = Mid(strFilter,6)
⇒同じ変数の名前のstrFilterがあり、Mid 6でどういう形で生成されるのでしょうか?
hatenaさんのアイデアも目からウロコです。年/月だけに固執してました。年/月から年/月/日に生成すればいいのですね。
DateAdd("m", 1, Me.[年/月形式])で最後の年/月には日がないけどDateAddで日付形式にしておけば例えば2023/10が勝手に2023/10/1と生成されるということなのですね。
連結フォームで、レコードソースはテーブルか更新可能なクエリの場合、下記の設定・状況の場合、詳細セクションのコントロールが表示されず真っ白になります。
「追加の許可」プロパティが「いいえ」
レコード件数が0(表示するデータがない)
新規レコードは追加しないという仕様のフォームの場合でレコード件数が0の場合は、そもそも何もすることがないのでフォームを開くこと自体無意味になります。
フォームの「開くとき」イベントでレコード件数が0の時は適切なメッセージ(例えば「表示するレコードがありません」)を表示させて開くのキャンセルしてはどうでしょうか。そうすれば、真っ白なフォームが表示されてびっくりするということはなくなります。
コード例
新規レコードを追加するのを許可するフォーム(追加の許可=はい)ではこのような現象はおきません。