ビギナーのものです。2023/04、2023/05等のフォーマット関数で作成した年月データがあるクエリを基に作成したフォームがあります。それの指定年月期間を抽出するコードが上手く出来ないため、やもえず下記の2つに分けてフィルターをしています。簡素化するために1つのコードにまとめるコードはどう記述したららいいでしょうか? コードのルールが分かっておらず、色々試しても出来ないので。
変数1 = "年月>= '" & Me.[コンボ年月1] & "'"
変数2 = "年月<= '" & Me.[コンボ年月2] & "'"
Me.Filter = 変数1 & " AND " & 変数2
通報 ...
そのまま右辺の内容を変数1、変数2の位置に記述すればそれでいいと思いますが
ただ、文字列リテラルをわざわざ分割して記述する必要はないので
"'" & " AND " & "年月<= '"
の部分は一つにまとめてもしくは、範囲を抽出するなら
BETWEEN n1 AND n2
を使ってなんていうのもありです
余談ですが、フィルタのコードは複雑化したりするのでバラしておいたほうが(コードを読み解く場合と合わせて)簡素だったりしますね
テンプレート的な記述で、この掲示板でも何度かこのようなコードを書いています
hirotonさんありがとうございました。質問が大きく変わって申しわけないのですが、このコンボで”実行時エラー2766” ”式が正しく入力されていないか、複雑すぎるために評価できません。たとえば、数式に複雑な要素が多すぎます。
変数に式の一部を割り当て、式を簡単にしてください。”が頻繁にでます(毎回ではなく普通に機能することもあります)。コードに問題あるのかなと思ってたのですがそうではなさそうです(すみません)。
コンボのSQLは下記です。
『SELECT Q買掛の支払締日と期日.支払締の年月
FROM Q買掛の支払締日と期日
WHERE (((Q買掛の支払締日と期日.買掛先ID)=[Forms]![F買掛仕入一覧]![cb取引先選択]) AND ((Q買掛の支払締日と期日.支払締の年月)>[Forms]![F買掛仕入一覧]![cb支払締の年月1] And (Q買掛の支払締日と期日.支払締の年月)<>"支払処理")) OR (((Q買掛の支払締日と期日.支払締の年月)>[Forms]![F買掛仕入一覧]![cb支払締の年月1] And (Q買掛の支払締日と期日.支払締の年月)<>"支払処理") AND (([Forms]![F買掛仕入一覧]![cb取引先選択]) Is Null))
GROUP BY Q買掛の支払締日と期日.支払締の年月
ORDER BY Q買掛の支払締日と期日.支払締の年月』
顧客の選択があるときは顧客+年月、選択ないときは年月のみでフィルターしています。
これが全然解決できなくて困っております・・・
元のデータ(テーブル構造)からみないと何ともですが、
『SELECT Q買掛の支払締日と期日.支払締の年月
FROM Q買掛の支払締日と期日
WHERE (((Q買掛の支払締日と期日.買掛先ID)=[Forms]![F買掛仕入一覧]![cb取引先選択]) AND ((Q買掛の支払締日と期日.支払締の年月)>[Forms]![F買掛仕入一覧]![cb支払締の年月1] And (Q買掛の支払締日と期日.支払締の年月)<>"支払処理")) OR (((Q買掛の支払締日と期日.支払締の年月)>[Forms]![F買掛仕入一覧]![cb支払締の年月1] And (Q買掛の支払締日と期日.支払締の年月)<>"支払処理") AND (([Forms]![F買掛仕入一覧]![cb取引先選択]) Is Null))
GROUP BY Q買掛の支払締日と期日.支払締の年月
ORDER BY Q買掛の支払締日と期日.支払締の年月』
下ではNullチェックすることがあるのに上では「=」比較をしているとか
『SELECT Q買掛の支払締日と期日.支払締の年月
FROM Q買掛の支払締日と期日
WHERE (((Q買掛の支払締日と期日.買掛先ID)=[Forms]![F買掛仕入一覧]![cb取引先選択]) AND ((Q買掛の支払締日と期日.支払締の年月)>[Forms]![F買掛仕入一覧]![cb支払締の年月1] And (Q買掛の支払締日と期日.支払締の年月)<>"支払処理")) OR (((Q買掛の支払締日と期日.支払締の年月)>[Forms]![F買掛仕入一覧]![cb支払締の年月1] And (Q買掛の支払締日と期日.支払締の年月)<>"支払処理") AND (([Forms]![F買掛仕入一覧]![cb取引先選択]) Is Null))
GROUP BY Q買掛の支払締日と期日.支払締の年月
ORDER BY Q買掛の支払締日と期日.支払締の年月』
「年月」データに対して「"支払処理"(なにかの文字列)」比較してるとかは首をかしげる部分ですね
とくに、Nullは扱いを間違うと不具合がよく出るので注意が必要です
「選択がない(=Null)ときは全データを対象にする」のはちょっとテクニックが必要ですが、たとえば
とかしてみたらどうでしょう?
hirotonさんありがとうございました。それにして試してみます。いける時とエラーがでるときがあるので。結果はまた連絡しますね。因みに”Nz([Forms]![F買掛仕入一覧]・・・”のNzはこの場合どういう意味合いになりますでしょうか?
「年月」データに対して「"支払処理"(なにかの文字列)」比較は・・・基クエリにIIFでそうしているのですが年/月と文字を一緒にしない方がいいでしょうか?
Nz 関数
1つ目のデータが
Null
なら2つ目のデータに置き換える関数です今回の場合
[Forms]![F買掛仕入一覧]![cb取引先選択]が選択済みのとき
[Forms]![F買掛仕入一覧]![cb取引先選択]が未選択(
Null
)のとき上記どちらかが実行されることになります。
買掛先ID=買掛先ID
は自分自身と比較すれば必ずTRUE
になる(※)ので、すなわち全データ。ということになります※買掛先IDが
NULL
の場合、正しく動作しませんデータベースでは「1つのフィールドに複数の情報を持たせない」というのが鉄則です
フィールド名のイメージと合わない情報が含まれるデータ構造は、なにかと厄介な問題を引き起こします
日付/時刻型のフィールドに対して例えば
Format([年月日],"yyyy/mm")
というような式を設定した演算フィールドがあるということでしょうか。だとしたら演算フィールドに対して抽出条件を設定するとインデックスが利用されないので、データ件数が多い場合は処理が重くなる可能性があります。
式は冗長になりますが、処理を少しでも高速にしたい場合は、演算フィールドにせずに下記のように生のフィールドに条件を設定するようにした方がいいです。(日付/時刻型フィールドにはインデックスを設定)
参考リンク
抽出条件でインデックスが無効になる場合 - hatena chips
(連絡が遅くなりました)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と生成されるということなのですね。
一応の確認ですが、プログラミングにおける(特にここの)「
=
」は代入を表していて「左の変数に右の計算結果を代入する」という動作をしますなので、
の形は、「元の変数の後ろに文字を追加する」記述になります
Me.Filter = Mid(strFilter,6)
についてMid 関数
「任意のフィルタを設けたい」時にコードが簡素に記述できます
フィルタを複数項目で連続してかける場合、それぞれの条件を
AND
でつなぎますが、「項目が任意」の場合、既にフィルタがあるかどうか(AND
をつけるかどうか)をチェックするのが煩わしくなってきますなので、フィルタをつける場合はとりあえず
AND
をつける、最後に先頭のAND
を除いた文字を取り出すという手順にして簡素化していますもう一つ、改修を見越してというのもあります
「フィルタ項目を追加したい」となった場合、上記のような問題を考える必要がでるので、もともと対応済みの記述にしておけば、「元のコードに手を付けず、追加したい項目の処理を追加するだけ」で済みます
「年月」データの持ち方について
パソコンは基本的に文字列よりも数値のほうが楽に扱えます。日付のデータも、表示上は書式設定にて「yyyy/mm/dd」形式で表示し、内部的には数値データとして持つ場合が多いです(シリアル値と呼ばれたりします)
この場合、関連する日付を数値計算で算出したり、日付計算用の関数が使えたりするというメリットが受けられます
ただし、必ず「日」のデータまで必要なので「年月」でいい場合には注意が必要です。「日」まで気にして範囲指定しないと漏れが出たりします(そのための計算で複雑化したりします)
現状であろう「年/月」の文字列で保存するとか、「yyyymm」を数値として保存するとかありますが、桁数(月の十の位の0詰め)に気を付ける必要があるとか出ますし、究極的には「年」「月」でフィールドを分けて保存するのが一番安全なのかなと思っています
hirotonさん すごく分かり易い説明ありがとうございました。自分では全くでてこないアイデアです。
日付を年・月・日に分けた場合(クエリでYear関数等で)、単独日付のフィルター式はANDで結合させて記述できるのですが範囲指定となるとどうもややこしくて、避けています。例えば2023・10・01 から 2024・01・31 は年月日が分割されたデータを使ってどうやってコード記述したら出来ますでしょうか?
前提として、「日」までデータに含むなら「年月日」で一つのデータにしたほうがいいです
不要部分を丁寧に除外してくとわかりやすいんじゃないかと思います
SQL直接記述するとなんてことないんですけど、クエリデザインで作ろうとしたらなかなか大変ですねこれ
hirotonさん ありがとうございます。クエリデザインよりもSQLで記述した方が簡単な事も多いのですね。SQL記述を勉強した方が今後いいですね。(今まで直接記述したことがないので)
何度もすみません、この関連で最後に1つだけ教えて欲しいのですが、年と月だけを使って(数字扱いで)範囲指定するにはどういうコードになりますでしょうか?(Filter)
例えば年の数値データが2023、2024とあり月の数値データが1~12があります。一つの年月なら下記で出来るのですが
strFilter1 = "年 =" & Me.[コンボ年1]
strFilter2 = "月 =" & Me.[コンボ月1]
Me.Filter = strFilter1 & " AND " & strFilter2
範囲となるとコードがややこしいもので。(コンボ年2、コンボ月2を範囲最後として)
上のSQL構文を元にたろうさんのデータでSQLビューでクエリを作成したあと、保存したクエリを開きなおしてみてください。デザインビューで表現する方法が表示されると思いますが、hirotonはサッと作れるようなデザインではなかったですね
あとは、任意項目でフィルタを掛けたい場合なんかはVBAで作ったほうが楽なので、やっぱりSQLそのものを覚えていくと出来ることが増えるでしょう
コードでフィルタを作る場合も、SQL構文のWHERE部分を作ればいいだけなので、上記の形でいいならそのまま、数字部分をコントロール参照に変えるだけですよ
1行で記述してもいいと思いますが、分割しながら記述するなら
ただ、元の質問をみると、「年」と「月」でコンボボックスを分けるのはあんまり望んでなさそうですよね
テーブル構造で「年」「月」分けた場合でも、コンボボックスのSQLでユーザー選択用に「年月」表記にして、コンボボックスは一つにしてもいいと思います
これで、コンボボックスの列数プロパティを「3」にすると、フォーム上の表記は「yyyy/mm」形式で、コードでは2列目、3列目を参照して「年」「月」のデータを直接使えるようになります
範囲の開始用を「cmb期間開始」、終了用を「cmb期間終了」とすることにして
コンボボックスの列指定まで入れるとコードが見にくくなるので、それらもそれぞれ変数に置くようにすると、SQL構築部分もすっきりしていいんじゃないかと思います
まぁ、どこまで細かく記述するか?は慣れと好みでいいところではあります
hirotonさん ありがとうございました。コードの事で strFilter & " AND NOT 年< 等でこのNOTはどういう意味になるのでしょうか? 理解力がなくてすみません。
既に説明しているところの繰り返しになりますが
を実現するためにはクエリ(SQL構文)で
を実行します
ACCESSの仕様として、フォーム上でフォームのレコードソースからさらにデータを抽出する場合、フィルタプロパティにSQLのWHERE句と同じ記述方法を指定することができます
フォームのプロパティはVBAから操作することができ、フィルタプロパティは
Me.Filter
で参照しとすれば、フィルタをかけることができます
なので、
NOT
はSQL構文で使うところのNOT
です。上記のWHERE
をNOT
が掛かる部分ごとに分けると次のようになりますNOT
は「否定」を表すので、「指定内容以外はOK」となります。つまり、「ここはダメ」を指定します。必要なだけ「ここはダメ」を重ねる(AND
で繋げる)と欲しい部分が残りますまぁ、SQLの記述方法はいろいろあるので、ここの方法は一つではありません。
NOT
を使わずに記述することもできるでしょう。そういうのも経験ですねhirotonさん ありがとうございました。消去方的な感じだったのですね。図まで用意して頂き大変分かり易かったです。お手数かけました。逆の発想が中々出来ず、自分自身がもどかしいところです。本当に色々とありがとうございました。