SELECT 氏名, Min(休暇取得日) AS 開始日, DMin("休暇取得日","クエリ15","氏名='" & 氏名 & "' And 休暇取得日>=#" & 休暇取得日 & "#") AS 終了日
FROM 素データ
GROUP BY 氏名, DMin("休暇取得日","クエリ15","氏名='" & 氏名 & "' And 休暇取得日>=#" & 休暇取得日 & "#");
Sub test()
Dim rs As Recordset
Dim 氏名 As String
Dim 終了日 As Date
Set rs = CurrentDb.OpenRecordset("SELECT 氏名, 休暇取得日 FROM 素データ ORDER BY 氏名, 休暇取得日;")
Debug.Print rs!氏名, rs!休暇取得日, ;
氏名 = rs!氏名
終了日 = rs!休暇取得日
rs.MoveNext
Do Until rs.EOF
If rs!氏名 <> 氏名 Then
Debug.Print 終了日
Debug.Print rs!氏名, rs!休暇取得日, ;
氏名 = rs!氏名
終了日 = rs!休暇取得日
rs.MoveNext
ElseIf rs!休暇取得日 = 終了日 + 1 Then
終了日 = 終了日 + 1
rs.MoveNext
Else
If Is休日(終了日 + 1) Then
終了日 = 終了日 + 1
Else
Debug.Print 終了日
Debug.Print rs!氏名, rs!休暇取得日, ;
終了日 = rs!休暇取得日
rs.MoveNext
End If
End If
Loop
Debug.Print 終了日
rs.Close
Set rs = Nothing
End Sub
Function Is休日(mydate As Date) As Boolean
If Weekday(mydate) = 1 Or Weekday(mydate) = 7 Then
Is休日 = True
Else
Is休日 = Is祝日(mydate)
End If
End Function
Function Is祝日(mydate As Date) As Boolean
'(略)
End Function
Private Sub Form_Error(DataErr As Integer, Response As Integer)
If DataErr = 3314 Then
Response = acDataErrContinue '既定のエラーメッセージを表示しない
MsgBox "入力必須なので空欄にはできません"
Me.ActiveControl.Undo
SendKeys "{TAB}" '次のコントロールへ移動
End If
End Sub
SELECT テーブル名.*
FROM テーブル名
WHERE
([都道府県]=[Forms]![Form名]![cmb1] Or [Forms]![Form名]![cmb1] Is Null)
And ([性別]=[Forms]![Form名]![cmb2] Or [Forms]![Form名]![cmb2] Is Null)
And ([既婚/未婚]=[Forms]![Form名]![cmb3] Or [Forms]![Form名]![cmb3] Is Null);
レコードの前後を比較するのはデータベースにとって苦手な処理です。複雑な処理を組むのも、その処理速度もデータベースにやらせるメリットはありません
このようなデータは、「計算で求められるデータ」であっても実データとして保存してしまうほうがシンプルに済みます
ただし、実データとして保存する場合は、それが常に最新のデータであると保証する必要があります。データの更新タイミングと、その内容を理論的に説明できるかということですね
カレンダーテーブルを作る
翌営業日の登録があるデータは連続休暇の最終日ではないので、翌営業日は何日か?という基礎データを作成します
内容は、全日付のカレンダーで、休日フラグ(土日祝日)を設けます。これで、翌営業日は「休日を除いて、対象の日付より大きく、最小の日付」として求められます。
Q素データ改
翌営業日が求められたら、翌営業日が存在するレコードは終了日ではないので、それを取り除くことにより終了日のリストを作成できます
Q終了日リスト
終了日のリストができたら、「自身の日付以上で最小の日付」が休暇期間の終了日になるので、素データのすべてのレコードに対して終了日を設定し、グループ化して休暇取得日の最小を取れば求めるデータになります
急に祝日が変わった(東京オリンピックのときとか)などに注意すれば変更することのないデータが元になるのでシンプルでしょう
また、「翌営業日」を求める計算は何度計算しても結果が変わらないのでカレンダーテーブルにフィールドを設けて保存してしまってもいいと思います
ありがとうございます。
おっしゃる通り、フッターを使う必然性はありませんでした。
解決いたしました。
確認です。
上記のフッターは、サブフォームのフッターですか。それとも、メインフォームのフッターですか。
構文には問題ありません。単純なデータのサブフォームと演算テキストボックスを用意して、メインフォームのテキストボックスから参照出来るか試してみました。
次のようなサイトもあります。
T'sWare Access Tips #556 サブフォームの集計値が取得できないときの#エラーの対処法
詳細はわかりませんが、これを読む限り、データベースを活用出来ていないように見えてしまいます。「テーブル構造(最低、2つのテーブル)を用いて何がやりたいのか」を明確に質問すれば、違うアドバイスが貰えるかもしれませんよ。
テーブルで値要求を「はい」にしておけば、入力必須としては完璧ですが、システムの仕様に依存するので、仕様を変更してユーザーインターフェイスを工夫しようとしてもどうしても制限があります。
下記で入力必須項目があるフォームのUIについて考察していますので、参考にしてみてください。
そのテキストボックスのタブオーダーが最後になってませんか。
最後だと、Enterキーでレコード保存してから次のレコードへ移動しようとしますが、Nullのままでは保存できないので、
MsgBox "入力必須なので空欄にはできません"
のメッセージが出て移動できません。コマンドボタン(例えばキャンセルボタン)のタブオーダーを最後にすれば、そのボタンにフォーカス移動します。(当方のサンプルで確認済み。)
詳細な解説ありがとうございました。理解するのに1時間以上かかりました。
フォームで表示対象者を変更させたり、休職者全件レポートなども動的に生成する必要がありますので、
テーブルに書き出して、これらの機能もいちおう実装できました。
hirotonさま、どうもありがとうございます。
結果セットとしては完璧でして、感動しています。
最後にコメントさせていただいた方法ですが、私が投稿前に考えたイメージになりますが、
段階的にでも、基幹システムから取得した素データに対して、休日データが補完できて、にフラグを立てられれば、
あとは表示の問題だけと考えておりました。
一気に結果を表示させるよりは、素データにクエリでフラグを立てて、表示変換をするほうが、
私には現実的と考えています(そもそも一気にそこまでできるのだと感動すらしているくらいでして)。
戻ってしまいますが、例えば、クエリとしてフラグなどの作業データを追加したりする方法についても、
少しアドナイスいただけましたら幸いです。
さて、本題です
「休日を挟んで連続していたら連続とみなす。そうでなかったら前のレコードの日付を終了日とする」ですね。上記丁寧版では無意味だった「終了日となりうる日付」と「休日を考慮した連続した日付の最終日」をきっちり分けて考えてあげればいいですね
肝となるのは
この部分ですね
変数のつくり方(数・名前)は非常に大事で、コードが複雑化する場合は、変数が適切か見直すと良いです。今回の
終了日
もコード上では実際の終了日を指すものではなく、一時的なデータとなっています(処理の結果、正式な終了日になる仮データ)コードの処理に即して名前を付けるのであれば「
終了日となりうる日付
」で、もっと具体的に言えば「休日を考慮した連続した日付の最終日
」ですこれを使ってコードを組みなおすと
処理を丁寧に書けばこんな感じです
こちらの意図として、「0」など不適当な値で更新できては困るフィールドであるため、Null(①前の状態)に戻せれば最良と考えています。
その際のエラーメッセージを抑制したいので、そのための手立てがあればご教示願いたいです。
連日ありがとうございます。
規定値を設定すれば、確かに解決しますね。最終手段として押さえておきます。
数値型フィールドで値要求を「はい」に設定するなら、規定値を設定しましょう。
フィールドを追加してときは規定値は0に設定されています。
結果テーブルを用意して、全部データ移行すれば。素データテーブルにこだわる意味はありますか?
迷走している様子を見ていると、そもそも何がやりたいのかをきちんと説明できるのか疑問です。
あまり現実的ではないかもしれませんが、私の理解できる範囲では、
1.氏名が空欄のところは、開始と終了に挟まれた期間の土日祝日のデータを補完し、
2.連番を振り、
3.連番最小値が開始日、連番最大値が終了日として認識
ここまで段階的にできれば何とかなりそうなのですが、土日、祝日の実テーブルを用意するにしても、
データの補完方法が分かりません。
アドバイスいただけましたら幸いです。
氏名 休暇取得日 連番 連番最小値 連番最大値
あああ 2022/8/4 1 True
あああ 2022/8/5 2
2022/8/6 3
2022/8/7 4
あああ 2022/8/8 5
あああ 2022/8/9 6
あああ 2022/8/10 7 True
あああ 2022/8/29 1 True
あああ 2022/8/30 2 True
あああ 2022/9/6 1 True
あああ 2022/9/7 2
あああ 2022/9/8 3
あああ 2022/9/9 4
2022/9/10 5
2022/9/11 6
あああ 2022/9/12 7 True
私の理解を超えていまして、直しどころが全然分かりませんでした。
例えば、
2022/9/16(金)休暇取得、2022/9/19(月)休暇取得があれば、9/17、9/18は休暇期間中と考え、
2022/9/16(金)休暇取得、2022/9/19(月)休暇取得がなければ、9/17、9/18は休暇期間中と考えない、
としたいと思っています。
どうもありがとうございます。
hirotonさまにご教授いただいたコードで期待通りの結果が得られたことを確認させていただきました。
一度にあの結果セットまでできなくても良いと考えていまして、
土日祝日の存在しないデータを埋められれば、キーブレイク処理等でできないことはないかなという感じでした。
素データの対象期間内の土日はWeekday関数を使って生成し、祝日はネットでデータを取得して作成していました。
これを、対象期間のうち開始と終了で挟まれた期間のみを、土日祝日の存在しないデータを埋められれば、
と苦戦していたところでした。
まずは、いただきましたロジックをじっくり勉強させていただきます。
上記のような流れです。
説明が下手で申し訳ありません。
①Nullのテキストボックス「Text1」をクリック
②「1」キーを押下
③「BackSpace」キーを押下
④「Tab」キーを押下
⑤1.~3.(4.)
この時点でテキストボックスはNullに
⑥「値がフィールドまたは……」
「3. の時点で Undo されますので、削除する前の値が表示されているはずです。」とのことですが、acFormAdd(=レコードの追加)ですので、値はNullに戻ります。
もう、一度確認しますが、
上記について、間違いないですか。
だとしたら、3. の時点で Undo されますので、削除する前の値が表示されているはずです。
その場合、EnterキーまたはTabキーで次のコントロールへ移動できるはずです。
試しにSendKeysの行をコメント化してみましたが、「値がフィールドまたは……」のメッセージは変わらず表示されます。
訂正
「値を入力しない限り別のコントロールにフォーカスを移すことができなくなります。」と記載の通り、コントロールを移動できなくなるために困っています。必ずしも次のコントロールへ移動することを求めているわけではありません。
例えば
その日が祝日かどうかを判断する方法はいろいろあるのでひとまず省略します
「存在しないデータ」を評価するのは大変です。処理中に適切に作り出す必要があります
上記例ではVBAで
終了日
として作り出してみました。処理の状況によってレコードを進めたり、足踏みしていたりと注意が必要です「休日テーブル」のような実データを用意する方向であれば、単純にテーブルをマージすれば日付が連続するデータになるのでクエリでもなんとかなるのかな?と思います。(そのために土・日の日付まで実データで保存するのはどうかと思いますが)
現状、どこまでできていますか。まったく手付かずですか。
祝日はどのように判断するつもりでしょうか。
休日テーブルに格納するのでしょうか。
まずは、どこまでできているか、やったことを提示してもらえますか。
手付かずでも、どのようにするのか考えていることを提示してもらえますか。
当方で作成したサンプルでは、次のコントロールへ移動も問題なくできていますが、SendKeysは環境によっては不安定な場合もあるようです。
次のコントロールへの移動は必須でしょうか。
入力必須のメッセージが出てあと、EnterキーかTabキーで移動できます。
削除したということはユーザーとしては変更の意思があるということなので、それを無視して次のコントロールへ勝手に移動するのは、ユーザーインターフェイスとしてどうかなと思いますが、どうでしょう。
1.から3.までは挙動を確認できています
4.については、フォーカスがText1から動いていないので、動作していないように見受けられますが、処理が停止しているわけではないようです。
試しに全ての行間および最終行にMsgBoxを挿入してみましたが、すべて表示されます。
「値がフィールドまたは……」のメッセージが表示されるのは、全ての処理が完了した後のようです。
既定のエラーメッセージの抑制はできているということですね。
私のコードの動作想定は、
ですが、どれが実行されてないですか。
エラーコード3314にあたる、「フィールドに値を入力してください。」という1つ目のメッセージは抑止できているようです。
はい。フォームプロパティの「エラー時」を[イベント プロシージャ]に変更し、ビルドボタンをクリックしてコードを記述しましたので。
フォームのデザインビューで、フォームプロパティの「エラー時」が
[イベント プロシージャ]
となってますか。また、そこのビルドボタンをクリックすると、前回の回答のコードが表示されますか。
早速のご回答、ありがとうございます。
私が記述したBeforeUpdate部をコメント化し、教えていただいたForm.Errorの記述を追加しましたが、それでも「値がフィールドまたはレコードの入力規則に違反しています。」のエラーが出てしまいました。
入力必須違反エラーは、フォームのエラー時イベントで拾えます。
Form.Error イベント (Access) | Microsoft Docs
hiroton様、hatena様
どうもありがとうございます。
解決いたしました。
主キーでも「規定値」プロパティが設定されているとNullでない場合もあります。
もし、その場合は、
NewRecordプロパティを参照すれば新規レコードかどうか判定できます。
新規レコードなら主キーがnullなので
みたいな形で
テキスト型のフィールドならば、
テーブルのデザインビューで、フィールドのプロパティを下記のように設定すると、
未入力は Null ではなく ""(空文字列)になります。
値要求 はい
空文字列の許可 はい
入力済みのフィールドは変わらないので、更新クエリか、置換で
Nullを""に更新します。
これで、質問の式か、下記の式でOKになります。
Like Nz([Forms]![Form名]![cmb1], '*')
ご提案ありがとうございます。
ちょっと、うまくいきませんでした。
この部分は、Nullにならないように、入力時にチェック機能をつけて、対応したいと思います
ありがとうございました。
前回の回答だと、インデックスが無効になるので、データ数が多いと重くなる場合があります。その場合は、下記のようにしてください。
SQLビューで下記のように記述します。
テーブル名、フィールド名は実際のものに変更してください。
保存するときは、SQLビューのまま保存してください。デザインビューにすると、複雑に書き換えられてしまうので、デザインビューにして保存しないようにしてください。