Microsoft Access 掲示板

views
4 フォロー
6,283 件中 2,241 から 2,280 までを表示しています。
12
hiroton 2022/09/20 (火) 16:14:13 20947@f966d

レコードの前後を比較するのはデータベースにとって苦手な処理です。複雑な処理を組むのも、その処理速度もデータベースにやらせるメリットはありません

このようなデータは、「計算で求められるデータ」であっても実データとして保存してしまうほうがシンプルに済みます

ただし、実データとして保存する場合は、それが常に最新のデータであると保証する必要があります。データの更新タイミングと、その内容を理論的に説明できるかということですね

カレンダーテーブルを作る

翌営業日の登録があるデータは連続休暇の最終日ではないので、翌営業日は何日か?という基礎データを作成します
内容は、全日付のカレンダーで、休日フラグ(土日祝日)を設けます。これで、翌営業日は「休日を除いて、対象の日付より大きく、最小の日付」として求められます。

翌営業日:DMin("日付", "カレンダー", "休日フラグ = False And 日付>#" & 休暇取得日 & "#")

Q素データ改

SELECT 氏名, 休暇取得日, DMin("日付","カレンダー","休日フラグ = False And 日付>#" & [休暇取得日] & "#")+0 AS 翌営業日
FROM 素データ;

翌営業日が求められたら、翌営業日が存在するレコードは終了日ではないので、それを取り除くことにより終了日のリストを作成できます

Q終了日リスト

SELECT Q素データ改.氏名, Q素データ改.休暇取得日
FROM Q素データ改 LEFT JOIN 素データ ON Q素データ改.翌営業日 = 素データ.休暇取得日
WHERE 素データ.休暇取得日 Is Null;

終了日のリストができたら、「自身の日付以上で最小の日付」が休暇期間の終了日になるので、素データのすべてのレコードに対して終了日を設定し、グループ化して休暇取得日の最小を取れば求めるデータになります

SELECT 氏名, Min(休暇取得日) AS 開始日, DMin("休暇取得日","クエリ15","氏名='" & 氏名 & "' And 休暇取得日>=#" & 休暇取得日 & "#") AS 終了日
FROM 素データ
GROUP BY 氏名, DMin("休暇取得日","クエリ15","氏名='" & 氏名 & "' And 休暇取得日>=#" & 休暇取得日 & "#");

急に祝日が変わった(東京オリンピックのときとか)などに注意すれば変更することのないデータが元になるのでシンプルでしょう
また、「翌営業日」を求める計算は何度計算しても結果が変わらないのでカレンダーテーブルにフィールドを設けて保存してしまってもいいと思います

3
もっちー 2022/09/19 (月) 15:13:27 ce1e4@39a0e

ありがとうございます。
おっしゃる通り、フッターを使う必然性はありませんでした。
解決いたしました。

2

確認です。

これをフッター上の別のテキストボックスで間接的に参照する指定の仕方がどうしても分かりません。

上記のフッターは、サブフォームのフッターですか。それとも、メインフォームのフッターですか。

1
りんご 2022/09/19 (月) 00:46:13 c564b@0e907

Forms![Frm_Main]![subForm1].Form![A]

 構文には問題ありません。単純なデータのサブフォームと演算テキストボックスを用意して、メインフォームのテキストボックスから参照出来るか試してみました。
 次のようなサイトもあります。
T'sWare Access Tips #556 サブフォームの集計値が取得できないときの#エラーの対処法

サブフォームのフッターに、カレントレコードの値を使って演算したテキストボックスをいくつかおいています。

 詳細はわかりませんが、これを読む限り、データベースを活用出来ていないように見えてしまいます。「テーブル構造(最低、2つのテーブル)を用いて何がやりたいのか」を明確に質問すれば、違うアドバイスが貰えるかもしれませんよ。

20

テーブルで値要求を「はい」にしておけば、入力必須としては完璧ですが、システムの仕様に依存するので、仕様を変更してユーザーインターフェイスを工夫しようとしてもどうしても制限があります。

下記で入力必須項目があるフォームのUIについて考察していますので、参考にしてみてください。

入力必須項目のある入力フォームのUIを考察する
Accessでは入力必須のフィールドは、テーブルのデザインで「値要求」を「はい」にするのが一番確実な方法です。今回は、ユーザーとってやさしいインタフェースという観点から入力フォームのデザインを考えてみます。...
Fc2

19
hatena 2022/09/17 (土) 10:58:09 修正

そのテキストボックスのタブオーダーが最後になってませんか。
最後だと、Enterキーでレコード保存してから次のレコードへ移動しようとしますが、Nullのままでは保存できないので、
MsgBox "入力必須なので空欄にはできません"のメッセージが出て移動できません。

コマンドボタン(例えばキャンセルボタン)のタブオーダーを最後にすれば、そのボタンにフォーカス移動します。(当方のサンプルで確認済み。)

11
もっちー 2022/09/17 (土) 10:47:58 ce1e4@39a0e

詳細な解説ありがとうございました。理解するのに1時間以上かかりました。

フォームで表示対象者を変更させたり、休職者全件レポートなども動的に生成する必要がありますので、
テーブルに書き出して、これらの機能もいちおう実装できました。

10
もっちー 2022/09/17 (土) 09:11:29 ce1e4@39a0e

hirotonさま、どうもありがとうございます。
結果セットとしては完璧でして、感動しています。

最後にコメントさせていただいた方法ですが、私が投稿前に考えたイメージになりますが、
段階的にでも、基幹システムから取得した素データに対して、休日データが補完できて、にフラグを立てられれば、
あとは表示の問題だけと考えておりました。

一気に結果を表示させるよりは、素データにクエリでフラグを立てて、表示変換をするほうが、
私には現実的と考えています(そもそも一気にそこまでできるのだと感動すらしているくらいでして)。
戻ってしまいますが、例えば、クエリとしてフラグなどの作業データを追加したりする方法についても、
少しアドナイスいただけましたら幸いです。

9
hiroton 2022/09/17 (土) 08:05:55 修正 f0ac9@2ee8f

さて、本題です

2022/9/16(金)休暇取得、2022/9/19(月)休暇取得があれば、9/17、9/18は休暇期間中と考え、
2022/9/16(金)休暇取得、2022/9/19(月)休暇取得がなければ、9/17、9/18は休暇期間中と考えない、

「休日を挟んで連続していたら連続とみなす。そうでなかったら前のレコードの日付を終了日とする」ですね。上記丁寧版では無意味だった「終了日となりうる日付」と「休日を考慮した連続した日付の最終日」をきっちり分けて考えてあげればいいですね

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
    '//休日分、判定する日付をずらす
        Dim 休暇期間の翌日 As Date
        休暇期間の翌日 = 終了日 + 1
        Do While(Is休日(休暇期間の翌日))
            休暇期間の翌日 = 休暇期間の翌日 + 1
        Loop

        If rs!休暇取得日 = 休暇期間の翌日 Then
            終了日 = 休暇期間の翌日
        Else
            Debug.Print 終了日
            Debug.Print rs!氏名, rs!休暇取得日, ;
            終了日 = rs!休暇取得日
        End If
    End If
'//レコード移動処理も一本化
    rs.MoveNext
Loop
8
hiroton 2022/09/17 (土) 07:39:39 f0ac9@2ee8f

肝となるのは

    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

この部分ですね

変数のつくり方(数・名前)は非常に大事で、コードが複雑化する場合は、変数が適切か見直すと良いです。今回の終了日もコード上では実際の終了日を指すものではなく、一時的なデータとなっています(処理の結果、正式な終了日になる仮データ)
コードの処理に即して名前を付けるのであれば「終了日となりうる日付」で、もっと具体的に言えば「休日を考慮した連続した日付の最終日」です

これを使ってコードを組みなおすと

'//前のレコードと比較して「次の日か?」
    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

処理を丁寧に書けばこんな感じです

18
大坊頭 2022/09/17 (土) 07:20:16 e4b95@dce72

こちらの意図として、「0」など不適当な値で更新できては困るフィールドであるため、Null(①前の状態)に戻せれば最良と考えています。
その際のエラーメッセージを抑制したいので、そのための手立てがあればご教示願いたいです。

17
大坊頭 2022/09/17 (土) 07:17:59 e4b95@dce72 >> 16

連日ありがとうございます。
規定値を設定すれば、確かに解決しますね。最終手段として押さえておきます。

16

数値型フィールドで値要求を「はい」に設定するなら、規定値を設定しましょう。
フィールドを追加してときは規定値は0に設定されています。

7
名前なし 2022/09/17 (土) 01:13:41 c564b@0e907

 結果テーブルを用意して、全部データ移行すれば。素データテーブルにこだわる意味はありますか?
 迷走している様子を見ていると、そもそも何がやりたいのかをきちんと説明できるのか疑問です。

6
もっちー 2022/09/17 (土) 00:25:20 ce1e4@39a0e

あまり現実的ではないかもしれませんが、私の理解できる範囲では、
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

5
もっちー 2022/09/17 (土) 00:11:38 ce1e4@39a0e

私の理解を超えていまして、直しどころが全然分かりませんでした。

例えば、
2022/9/16(金)休暇取得、2022/9/19(月)休暇取得があれば、9/17、9/18は休暇期間中と考え、
2022/9/16(金)休暇取得、2022/9/19(月)休暇取得がなければ、9/17、9/18は休暇期間中と考えない、
としたいと思っています。

3
もっちー 2022/09/16 (金) 23:09:10 ce1e4@39a0e

どうもありがとうございます。
hirotonさまにご教授いただいたコードで期待通りの結果が得られたことを確認させていただきました。

一度にあの結果セットまでできなくても良いと考えていまして、
土日祝日の存在しないデータを埋められれば、キーブレイク処理等でできないことはないかなという感じでした。

素データの対象期間内の土日はWeekday関数を使って生成し、祝日はネットでデータを取得して作成していました。
これを、対象期間のうち開始と終了で挟まれた期間のみを、土日祝日の存在しないデータを埋められれば、
と苦戦していたところでした。

まずは、いただきましたロジックをじっくり勉強させていただきます。

15
大坊頭 2022/09/16 (金) 22:55:41 e4b95@62c21 >> 13

上記のような流れです。
説明が下手で申し訳ありません。

14
大坊頭 2022/09/16 (金) 22:49:01 e4b95@62c21 >> 13

①Nullのテキストボックス「Text1」をクリック
②「1」キーを押下
③「BackSpace」キーを押下
④「Tab」キーを押下
⑤1.~3.(4.)
この時点でテキストボックスはNullに
⑥「値がフィールドまたは……」

13
大坊頭 2022/09/16 (金) 22:44:18 e4b95@62c21 >> 12

「3. の時点で Undo されますので、削除する前の値が表示されているはずです。」とのことですが、acFormAdd(=レコードの追加)ですので、値はNullに戻ります。

12

もう、一度確認しますが、

1.から3.までは挙動を確認できています

上記について、間違いないですか。

だとしたら、3. の時点で Undo されますので、削除する前の値が表示されているはずです。
その場合、EnterキーまたはTabキーで次のコントロールへ移動できるはずです。

11
大坊頭 2022/09/16 (金) 17:29:25 e4b95@62c21 >> 9

試しにSendKeysの行をコメント化してみましたが、「値がフィールドまたは……」のメッセージは変わらず表示されます。

10
大坊頭 2022/09/16 (金) 17:28:48 e4b95@62c21 >> 9

訂正
「値を入力しない限り別のコントロールにフォーカスを移すことができなくなります。」と記載の通り、コントロールを移動できなくなるために困っています。必ずしも次のコントロールへ移動することを求めているわけではありません。

9
大坊頭 2022/09/16 (金) 17:26:12 e4b95@62c21 >> 8

値を入力しない限り別のコントロールにフォーカスを移すことができなくなります。
コントロールを移動できなくなるために困っています。必ずしも次のコントロールへ移動することを求めているわけではありません。

2
hiroton 2022/09/16 (金) 17:13:13 b1aad@f966d

例えば

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

その日が祝日かどうかを判断する方法はいろいろあるのでひとまず省略します


「存在しないデータ」を評価するのは大変です。処理中に適切に作り出す必要があります

上記例ではVBAで終了日として作り出してみました。処理の状況によってレコードを進めたり、足踏みしていたりと注意が必要です

「休日テーブル」のような実データを用意する方向であれば、単純にテーブルをマージすれば日付が連続するデータになるのでクエリでもなんとかなるのかな?と思います。(そのために土・日の日付まで実データで保存するのはどうかと思いますが)

1

現状、どこまでできていますか。まったく手付かずですか。

祝日はどのように判断するつもりでしょうか。
休日テーブルに格納するのでしょうか。

まずは、どこまでできているか、やったことを提示してもらえますか。
手付かずでも、どのようにするのか考えていることを提示してもらえますか。

8

当方で作成したサンプルでは、次のコントロールへ移動も問題なくできていますが、SendKeysは環境によっては不安定な場合もあるようです。

次のコントロールへの移動は必須でしょうか。

入力必須のメッセージが出てあと、EnterキーかTabキーで移動できます。
削除したということはユーザーとしては変更の意思があるということなので、それを無視して次のコントロールへ勝手に移動するのは、ユーザーインターフェイスとしてどうかなと思いますが、どうでしょう。

7
大坊頭 2022/09/16 (金) 14:39:18 ef5e5@9454b >> 4

1.から3.までは挙動を確認できています
4.については、フォーカスがText1から動いていないので、動作していないように見受けられますが、処理が停止しているわけではないようです。
試しに全ての行間および最終行にMsgBoxを挿入してみましたが、すべて表示されます。
「値がフィールドまたは……」のメッセージが表示されるのは、全ての処理が完了した後のようです。

6

既定のエラーメッセージの抑制はできているということですね。

私のコードの動作想定は、

  1. 既存のメッセージを抑制。
  2. 代わりに、"入力必須なので空欄にはできません"とメッセージを表示。
  3. Undoで削除を取り消して元に戻す
  4. 次のコントロールへ移動

ですが、どれが実行されてないですか。

5
大坊頭 2022/09/15 (木) 21:10:23 e4b95@e5a6d >> 4

エラーコード3314にあたる、「フィールドに値を入力してください。」という1つ目のメッセージは抑止できているようです。

4
大坊頭 2022/09/15 (木) 20:40:45 e4b95@e5a6d >> 3

はい。フォームプロパティの「エラー時」を[イベント プロシージャ]に変更し、ビルドボタンをクリックしてコードを記述しましたので。

3

フォームのデザインビューで、フォームプロパティの「エラー時」が [イベント プロシージャ] となってますか。
また、そこのビルドボタンをクリックすると、前回の回答のコードが表示されますか。

画像1

2
大坊頭 2022/09/15 (木) 13:49:20 e4b95@e5a6d >> 1

早速のご回答、ありがとうございます。
私が記述したBeforeUpdate部をコメント化し、教えていただいたForm.Errorの記述を追加しましたが、それでも「値がフィールドまたはレコードの入力規則に違反しています。」のエラーが出てしまいました。

1

入力必須違反エラーは、フォームのエラー時イベントで拾えます。

Form.Error イベント (Access) | Microsoft Docs

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
3
anjera 2022/09/09 (金) 12:26:19 ccf1e@39a0e

hiroton様、hatena様

どうもありがとうございます。
解決いたしました。

2

主キーでも「規定値」プロパティが設定されているとNullでない場合もあります。
もし、その場合は、
NewRecordプロパティを参照すれば新規レコードかどうか判定できます。

=IIf([NewRecord], Null, DLookup())
1
hiroton 2022/09/09 (金) 08:12:22 22d55@f966d

新規レコードなら主キーがnullなので

=IIf(主キー Is Null,,Dlookup((略)))

みたいな形で

7

テキスト型のフィールドならば、
テーブルのデザインビューで、フィールドのプロパティを下記のように設定すると、
未入力は Null ではなく ""(空文字列)になります。

値要求 はい
空文字列の許可 はい

入力済みのフィールドは変わらないので、更新クエリか、置換で
Nullを""に更新します。

これで、質問の式か、下記の式でOKになります。

Like Nz([Forms]![Form名]![cmb1], '*')

6
nokonoko 2022/09/05 (月) 13:31:20 e1bac@54883

ご提案ありがとうございます。
ちょっと、うまくいきませんでした。

この部分は、Nullにならないように、入力時にチェック機能をつけて、対応したいと思います
ありがとうございました。

5

前回の回答だと、インデックスが無効になるので、データ数が多いと重くなる場合があります。その場合は、下記のようにしてください。

SQLビューで下記のように記述します。

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);

テーブル名、フィールド名は実際のものに変更してください。

保存するときは、SQLビューのまま保存してください。デザインビューにすると、複雑に書き換えられてしまうので、デザインビューにして保存しないようにしてください。