• 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関数使うのもありですね。
    勉強になりました。