いつも大変お世話になってます。今回はユーザー定義関数です。
簡単なユーザー関数はなんとか出来る様になったのですが、少し複雑になるとこんがらがってしまって・・・
現在クエリには下記の記述で締め日付と支払期日を求められています。何度か使う事がありますのでこれをユーザー関数にしたいのです。
締め日付: IIf([締め実日] Is Null,[売上日付],DateSerial(Year([売上日付]),Month([売上日付])+IIf(Day([売上日付])>[締め実日],1,0),[締め実日]))
支払期日: IIf([支払月間] Is Null,DateAdd("d",[支払日間],[締め日付]),DateSerial(Year([締め日付]),Month([締め日付])+[支払月間]+IIf([支払実日]=0,1,0),[支払実日]))
参照元(これらを組み合わせてクエリにしてます)は下記です
テーブル:T締め日一覧 [締め日ID], [締め日](末日、20日等のテキスト) ,[締め実日](0,20等の数値)
テーブル:T支払日一覧 [支払日ID], [支払日](翌月末日、翌々月10日等のテキスト) ,[支払月間](1,2等の数値),[支払実日](0,20等の数値),[支払日間](10等の数値)
テーブル:T出荷Main/Sub には顧客ID、売上日付等
関数に簡潔にまとめるにはどんな記述になりますでしょうか? よろしくお願いします。
コード(SQL構文、計算式等)をそのまま記述するとこの掲示板用のMarkdown記述と解釈されて意図しない整形がされてしまうことがあるので、コードブロックを使って記述しましょう
もっとも単純には、計算式で指定しているフィールドを重複を省いて引数において、そのまま戻り値に計算式を指定し、フィールド部分を引数に置き換えればユーザー定義関数化ができます
※VBAで解釈できない計算式(SQL記述)の場合はこの方法ではうまくいかない場合があります
※VBA記述のマナーは無視しているので、ユーザー定義関数がうまく動作することが確認出来たらVBA記述として望ましい記述か?を別途見直してみるとよいです
クエリで使うときはそれぞれ
または、締め日付を求めずに支払期日を求める場合は、支払期日の締め日付指定部分に締め日付を求める関数を指定すればよいでしょう
hirotonさん ありがとうございます。
”※VBAで解釈できない計算式(SQL記述)の場合はこの方法ではうまくいかない場合があります
※VBA記述のマナーは無視しているので、ユーザー定義関数がうまく動作することが確認出来たらVBA記述として望ましい記述か?を別途見直してみるとよいです”とありますが、マナー無視している事になるのでしょうか?(すみません知識不足で)
VBAは、Is Null は使えないので、代わりにIsNull関数を使います。
あと、VBAを使うならIf構文を使った方が読みやすいように思います。
※VBAで解釈できない計算式(SQL記述)の場合はこの方法ではうまくいかない場合があります
ろくに計算式を見ないで回答してしまっていてアレなんですが、今回の内容だけでも
NULL
の判定はVBA用の記述にする必要がありますね※VBA記述のマナーは無視しているので~
VBA記述には省略可能なものがありますが、記述しておくのが一般的なものや、問題なくとも意味合いが正確でないものもあります
Function ステートメント
ざっくりいうと、データ型はしっかり記述したほうが良いです
締め実日はNULLを扱うため、
Variant
型である必要があります。また、データ型の省略時はVariant
型となるため、省略しても同じですが、明示することにより、NULL
を扱うことがあるし、それを意図してやっているということが読み取れるようになります逆に、そのほかの引数についてはしっかりそれぞれのデータ型を使うと指定(制限)をしておきましょう
関数の戻り値のデータ型
Function 締め日付(締め実日 As Variant, 売上日付 As Date) As Date
を記述するかどうかは個人差が出そうですが、日付型なら日付型を指定しておくと諸所で日付型のデータとして扱ってくれるのでよりよいと思われます(文字配置の「標準」が右寄せになったりとか)
あと、変数の名前からデータの内容がいまいち正確でないものには何か手を入れたほうがいいです
「日付」に「0日」はありませんし、「0の指定が1の1つ前ではない」ので、直感と実態が合わなくなります。末日指定をどうデータベース化するか?は悩みどころではありますが、とりあえず今回はコメント記述で対応ということで
あとはまぁ、「1行で記述する必要はない」ので、入れ子になりすぎて見づらい記述を分解するとかですかね。どこまでやるかはその時の内容次第です
質問文を頑張って読み取って「支払実日の0は末日指定なのだろう」と思ってますが合ってますよね?
色々詳細をありがとうございました。支払実日の0は末日になります。関数に出来ました。
”Else
締め日付 = DateSerial(Year(売上日付), Month(売上日付), 締め実日)
If Day(売上日付) > 締め実日 Then 締め日付 = DateAdd("m", 1, 締め日付) ” の様な記述ができるのですね。初めて知りました。この場合Ifが2つあってもEnd Ifは1つなのですね。
If文は複数行で書く構文と、1行で書く構文があります。
1行構文
複数行構文
条件が成立するときに実行するときのステートメントが一つのときは1行で書くことができます。(複数行でかいてもいいですが。)
つまり、
は、下記と同等です。
hirotonさんから適切なアドバイスがありますが、変数の型は省略しない方がいいというのは私も同意見です。
Accessの場合は、フィールドの値を引数とすることが多くなるので、フィールドのデータ型やプロパティ設定と合わせるのがいいでしょう。
Null値になる可能性がある(値要求=いいえの場合)ならVariant型、
Null値になる可能性がない(値要求=はいの場合)ならフィールドのデータ型と同じにする。
戻り値の型もNull値を返す必要性がある場合はVariant型、Null値を返す必要性がない場合は想定する型で宣言します。
hatenaさん ありがとうございました。勉強になります。
関数結果を細かく見ると10/31(正)が10/30(誤)になったりしてましたが、下記で正確にいけました。
”If Day(売上日付) > 締め実日 Then 締め日付 = DateSerial(Year(売上日付), Month(売上日付) +1,締め実日)”
ちょっと焦りましたが、これでいけました。
大変お世話になりました。
その後、期日の方も下記で出来ました(少し苦労しましたが)
”Function 支払期日(支払月間 As Variant, 支払日間 As Variant, 締め日付 As Date, 支払実日 As Variant) As Date
If IsNull([支払月間]) Then
支払期日 = DateAdd("d", [支払日間], [締め日付])
Else
支払期日 = DateSerial(Year([締め日付]), Month([締め日付]) + [支払月間], [支払実日])
If [支払実日] = 0 Then 支払期日 = DateSerial(Year([締め日付]), Month([締め日付]) + [支払月間] + 1, [支払実日])
End If
End Function”
hirotonさん色々と貴重なアドバイスありがとうございました。hatenaさんも毎回ありがとうございます。
支払実日 = 0
のとき、1行目の処理に意味がない(直後に支払期日
が上書きされる)のと、支払実日 = 0
のときは支払実日
は「0」でしかないのでのほうがきれいな書き方です
または、結局似たような計算式を並べるだけになっているので、この部分に限っては
IIf
を使ってでもいいと思います。
もしくは、共通部分以外を変数を用意して事前に計算する
なんてやり方もあります
hirotonさんありがとうございました。(返信遅くなりすみませんでした)
確かに月末日を求めるのに支払月間に+1するかどうかなのでIIf関数使うのもありですね。
勉強になりました。