Microsoft Access 掲示板

締日・支払期日をユーザー定義関数で作りたい

11 コメント
133 views
4 フォロー

いつも大変お世話になってます。今回はユーザー定義関数です。
簡単なユーザー関数はなんとか出来る様になったのですが、少し複雑になるとこんがらがってしまって・・・
現在クエリには下記の記述で締め日付と支払期日を求められています。何度か使う事がありますのでこれをユーザー関数にしたいのです。

締め日付: 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、売上日付等

 関数に簡潔にまとめるにはどんな記述になりますでしょうか? よろしくお願いします。

beginner
作成: 2025/02/18 (火) 16:39:32
最終更新: 2025/02/18 (火) 18:09:45
通報 ...
1
hiroton 2025/02/18 (火) 17:51:38 1b258@f966d

コード(SQL構文、計算式等)をそのまま記述するとこの掲示板用のMarkdown記述と解釈されて意図しない整形がされてしまうことがあるので、コードブロックを使って記述しましょう


もっとも単純には、計算式で指定しているフィールドを重複を省いて引数において、そのまま戻り値に計算式を指定し、フィールド部分を引数に置き換えればユーザー定義関数化ができます

Function 締め日付(締め実日, 売上日付)
    締め日付 = IIf(締め実日 Is Null, 売上日付, DateSerial(Year(売上日付), Month(売上日付) + IIf(Day(売上日付) > 締め実日, 1, 0), 締め実日))
End Function
Function 支払期日(支払月間, 支払日間, 締め日付, 支払実日)
    支払期日 = IIf([支払月間] Is Null, DateAdd("d", [支払日間], [締め日付]), DateSerial(Year([締め日付]), Month([締め日付]) + [支払月間] + IIf([支払実日] = 0, 1, 0), [支払実日]))
End Function

※VBAで解釈できない計算式(SQL記述)の場合はこの方法ではうまくいかない場合があります
※VBA記述のマナーは無視しているので、ユーザー定義関数がうまく動作することが確認出来たらVBA記述として望ましい記述か?を別途見直してみるとよいです

クエリで使うときはそれぞれ

締め日付: 締め日付([締め実日],[売上日付])
支払期日: 支払期日([支払月間],[支払日間],[締め日付],[支払実日])

または、締め日付を求めずに支払期日を求める場合は、支払期日の締め日付指定部分に締め日付を求める関数を指定すればよいでしょう

支払期日: 支払期日([支払月間],[支払日間],締め日付([締め実日],[売上日付]),[支払実日])
2
beginner 2025/02/19 (水) 08:41:24 61dd6@f300d

hirotonさん ありがとうございます。
”※VBAで解釈できない計算式(SQL記述)の場合はこの方法ではうまくいかない場合があります
※VBA記述のマナーは無視しているので、ユーザー定義関数がうまく動作することが確認出来たらVBA記述として望ましい記述か?を別途見直してみるとよいです”とありますが、マナー無視している事になるのでしょうか?(すみません知識不足で)

3
hatena 2025/02/19 (水) 09:41:11 修正 >> 2

VBAは、Is Null は使えないので、代わりにIsNull関数を使います。

Function 締め日付(締め実日, 売上日付)
    締め日付 = IIf(IsNull(締め実日), 売上日付, DateSerial(Year(売上日付), Month(売上日付) + IIf(Day(売上日付) > 締め実日, 1, 0), 締め実日))
End Function

あと、VBAを使うならIf構文を使った方が読みやすいように思います。

Function 締め日付(締め実日, 売上日付)
    If IsNull(締め実日) Then
        締め日付 = 売上日付
    Else
        締め日付 = DateSerial(Year(売上日付), Month(売上日付), 締め実日)
        If Day(売上日付) > 締め実日 Then 締め日付 = DateAdd("m", 1, 締め日付)
    End If
End Function
4
hiroton 2025/02/19 (水) 10:09:39 116a6@f966d

※VBAで解釈できない計算式(SQL記述)の場合はこの方法ではうまくいかない場合があります
ろくに計算式を見ないで回答してしまっていてアレなんですが、今回の内容だけでもNULLの判定はVBA用の記述にする必要がありますね

締め実日 Is Null
↓
IsNull(締め実日)

※VBA記述のマナーは無視しているので~
VBA記述には省略可能なものがありますが、記述しておくのが一般的なものや、問題なくとも意味合いが正確でないものもあります

Function ステートメント
ざっくりいうと、データ型はしっかり記述したほうが良いです

Function 締め日付(締め実日, 売上日付)
↓
Function 締め日付(締め実日 As Variant, 売上日付 As Date) As Date

締め実日はNULLを扱うため、Variant型である必要があります。また、データ型の省略時はVariant型となるため、省略しても同じですが、明示することにより、NULLを扱うことがあるし、それを意図してやっているということが読み取れるようになります
逆に、そのほかの引数についてはしっかりそれぞれのデータ型を使うと指定(制限)をしておきましょう

関数の戻り値のデータ型

Function 締め日付(締め実日 As Variant, 売上日付 As Date) As Date

を記述するかどうかは個人差が出そうですが、日付型なら日付型を指定しておくと諸所で日付型のデータとして扱ってくれるのでよりよいと思われます(文字配置の「標準」が右寄せになったりとか)

あと、変数の名前からデータの内容がいまいち正確でないものには何か手を入れたほうがいいです

Function 支払期日(支払月間, 支払日間, 締め日付, 支払実日)
↓
Function 支払期日(支払月間, 支払日間, 締め日付, 支払実日)
'支払実日が「0」は末日指定を表す

「日付」に「0日」はありませんし、「0の指定が1の1つ前ではない」ので、直感と実態が合わなくなります。末日指定をどうデータベース化するか?は悩みどころではありますが、とりあえず今回はコメント記述で対応ということで

あとはまぁ、「1行で記述する必要はない」ので、入れ子になりすぎて見づらい記述を分解するとかですかね。どこまでやるかはその時の内容次第です

5
hiroton 2025/02/19 (水) 10:20:41 116a6@f966d

質問文を頑張って読み取って「支払実日の0は末日指定なのだろう」と思ってますが合ってますよね?

6
beginner 2025/02/19 (水) 12:56:02 61dd6@f300d

色々詳細をありがとうございました。支払実日の0は末日になります。関数に出来ました。
”Else
        締め日付 = DateSerial(Year(売上日付), Month(売上日付), 締め実日)
        If Day(売上日付) > 締め実日 Then 締め日付 = DateAdd("m", 1, 締め日付) ” の様な記述ができるのですね。初めて知りました。この場合Ifが2つあってもEnd Ifは1つなのですね。

7

If文は複数行で書く構文と、1行で書く構文があります。
1行構文

If 条件式 Then 真の処理 [Else 偽の処理]

複数行構文

If 条件式 Then
  真の処理 ・・・ 条件式を満たした場合の処理
[Else
  偽の処理] ・・・ 条件式を満たさない場合の処理
End If

条件が成立するときに実行するときのステートメントが一つのときは1行で書くことができます。(複数行でかいてもいいですが。)

つまり、

        If Day(売上日付) > 締め実日 Then 締め日付 = DateAdd("m", 1, 締め日付) 

は、下記と同等です。

        If Day(売上日付) > 締め実日 Then
             締め日付 = DateAdd("m", 1, 締め日付) 
        End If

hirotonさんから適切なアドバイスがありますが、変数の型は省略しない方がいいというのは私も同意見です。

Accessの場合は、フィールドの値を引数とすることが多くなるので、フィールドのデータ型やプロパティ設定と合わせるのがいいでしょう。

Null値になる可能性がある(値要求=いいえの場合)ならVariant型、
Null値になる可能性がない(値要求=はいの場合)ならフィールドのデータ型と同じにする。

戻り値の型もNull値を返す必要性がある場合はVariant型、Null値を返す必要性がない場合は想定する型で宣言します。

8
beginner 2025/02/19 (水) 14:31:09 61dd6@f300d

hatenaさん ありがとうございました。勉強になります。
関数結果を細かく見ると10/31(正)が10/30(誤)になったりしてましたが、下記で正確にいけました。
”If Day(売上日付) > 締め実日 Then 締め日付 =  DateSerial(Year(売上日付), Month(売上日付) +1,締め実日)”
ちょっと焦りましたが、これでいけました。
大変お世話になりました。

9
beginner 2025/02/19 (水) 16:05:03 61dd6@f300d

その後、期日の方も下記で出来ました(少し苦労しましたが)
”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さんも毎回ありがとうございます。

10
hiroton 2025/02/21 (金) 10:15:10 bb150@f966d

           支払期日 = DateSerial(Year([締め日付]), Month([締め日付]) + [支払月間], [支払実日])
           If [支払実日] = 0 Then 支払期日 = DateSerial(Year([締め日付]), Month([締め日付]) + [支払月間] + 1, [支払実日])

支払実日 = 0のとき、1行目の処理に意味がない(直後に支払期日が上書きされる)のと、支払実日 = 0のときは支払実日は「0」でしかないので

           If 支払実日 = 0 Then
               支払期日 = DateSerial(Year(締め日付), Month(締め日付) + 支払月間 + 1, 0)
           Else
               支払期日 = DateSerial(Year(締め日付), Month(締め日付) + 支払月間, 支払実日)
           End If

のほうがきれいな書き方です

または、結局似たような計算式を並べるだけになっているので、この部分に限ってはIIfを使って

支払期日 = DateSerial(Year(締め日付), Month(締め日付) + 支払月間 + IIf(支払実日 = 0, 1, 0), 支払実日)

でもいいと思います。

もしくは、共通部分以外を変数を用意して事前に計算する

Dim 締め月 As Long
締め月 = Month(締め日付) + 支払月間 + IIf(支払実日 = 0, 1, 0)
支払期日 = DateSerial(Year(締め日付), 締め月, 支払実日)

なんてやり方もあります

11
beginner 2025/03/04 (火) 08:47:03 61dd6@5d7f7

hirotonさんありがとうございました。(返信遅くなりすみませんでした)
確かに月末日を求めるのに支払月間に+1するかどうかなのでIIf関数使うのもありですね。
勉強になりました。