Microsoft Access 掲示板

フォームでの入力規制

10 コメント
views
4 フォロー

ACCESS勉強中のものです(初心者レベル)。
メイン・サブフォームがあり、メインにはユーザー等の入力をしてサブにはテーブルにある商品をコンボで選ぶ形にしてます。(その他数量等の入力あり)。商品には個々に区分けが数種あり(仮にA・B・C等)それは商品テーブルに各入力されています。
サブフォームに一番最初に区分けAの商品を選んだ場合、BやC(つまりA以外)が選べない様にしたいのです。メッセージボックスで警告を出し規制をかけたいのです。フォームのページ毎に一つの商品区分け分商品しか選択出来ない様にしたいのです。
どうしたら簡単にそんな事が出来るのかと思いまして。組立がわからなくて・・・
よろしくお願いします。

KEN
作成: 2023/06/08 (木) 15:36:58
通報 ...
1

メインフォームのレコードソースはテーブルはユーザー情報を格納するもの。
サブフォームのレコードソースはそのユーザーが購入する商品データを入力するもの。
ということですか。

できれば、関係するテーブルのテーブル名とそのフィールド構成を提示してもらえますか。
少なくとも下記の3つのテーブルが必要なはずです。
ユーザー情報のテーブル
商品購入データのテーブル
商品情報のテーブル

データーベースはまずは、テーブル設計が重要でそれがまちがっていたら、使い物にならないので。

。フォームのページ毎に一つの商品区分け分商品しか選択出来ない様にしたいのです。

フォームにページという概念はないのですが、どのような意味が使っていますか。

2
hiroton 2023/06/09 (金) 09:32:37 b43ba@f966d

何をもって簡単と言うかわかりませんが
コンボボックスの入力規則に以下を設定します

=IIf(DCount("*","サブテーブル","リンクフィールド=" & [リンクフィールド])<=1,[商品],DLookUp("商品","商品テーブル","区分け='" & DLookUp("区分け","商品テーブル","商品=" & DLookUp("商品","サブテーブル","リンクフィールド=" & [リンクフィールド])) & "' AND 商品=" & [商品]))

※各項目は実際のフォームの内容に合わせる必要があります

3

構成は下記です。
1.ユーザーテーブル:●ユーザーID(主キー) ●ユーザー名
2.商品テーブル:●商品ID(主キー) ●商品名 ●区分け
3.取引メインテーブル:●取引ID(主キー) ●日付 ●ユーザー選択(コンボにて)
4.取引明細テーブル(サブ):●明細ID(主キー) ●商品選択(コンボにて) 他数量等 ●取引ID
この3と4を使い(取引IDを1対多でリレーション)して親子フォームを作成しています。
フォームのページというのは私が勝手に認識してました。取引ID毎の親子フォームになっているもので。
取引入力時にその時のサブフォームで一番最初に選択した商品区分け以外の区分けを選ぼうとした場合に警告を出しキャンセルさせたいのです。少し前からこのフォームで処理していたのですが事情がかわり取引毎に同一区分け商品だけが紐付けしなくてはいけなくなったので(過去に入力されたものはいいのですが、今後で)

hiroton さん早速ありがとうございます。関数単体の意味はわかるのですが全体の処理はどういう動きでしょうか?(長い関数式が慣れてなくて、すみません)

宜しくお願いします。

4
hiroton 2023/06/09 (金) 11:48:13 b43ba@f966d

関数は「機能をひとまとめにしたもの」なので、そこだけ抜き出せば何らかの値に置き換わります。複雑な計算式はメモ帳などで整形してみると理解しやすくなります

┌=IIf(
│ ┌DCount("*"
│ │,"取引明細テーブル"
│ │,"取引ID=" & [取引ID]
│ └)<=1
│,[商品ID]
│,┌DLookUp("商品ID"
│ │,"商品テーブル"
│ │,"区分け='" & 
│ │ ┌DLookUp("区分け"
│ │ │,"商品テーブル"
│ │ │,"商品ID=" & 
│ │ │ ┌DLookUp("商品ID"
│ │ │ │,"取引明細テーブル"
│ │ │ │,"取引ID=" & [取引ID] & " AND 明細ID <> " & [明細ID]
│ │ │ └)
│ │ └) & "' AND 商品ID=" & [商品ID]
│ └)
└)

※区分け取得部分のDLookup関数を調整

サブフォームに一番最初に区分けAの商品を選んだ場合、BやC(つまりA以外)が選べない様にしたい

これをプログラミング的な思考に置き換えていきます

本手法は入力規則を使うことにしたので、「入力可能な商品IDかどうかを判定し、入力可能であればその商品IDを入力規則の値にする」様な計算式を作成します

まず、「サブフォームに一番最初に区分けAの商品を選んだ」の時点で「最初の入力」の場合と「2回目以降の入力」で別な処理を行う必要が出ます。つまり、「何の制限もない初回入力」と「制限のかかる2回目以降」を作る必要があります

'//サブフォームのデータ単位で最初の入力と2回目以降の入力を切り分ける処理
┌=IIf(
│ ┌DCount("*"
│ │,"取引明細テーブル"
│ │,"取引ID=" & [取引ID]
│ └)<=1
│,(最初の入力)
│,(2回目以降の入力)
└)

「(最初の入力)」はどんな商品IDでも入力できる必要があるので、入力規則としては「どんな商品IDが指定されても同じ商品IDになるような式」とします。つまり自分自身を設定すれば解決です

「(2回目以降の入力)」は、「自身の区分け」と「入力済みデータの区分け」が同じかどうかを判定し、処理を分岐(そのまま入力、または、入力規則のエラーとしてメッセージ表示)させます
ただ、入力規則を使う関係上、「同じかどうか」だけでなく、「同じ場合は自身の商品ID」という結果が必要になります。そこで、DLookup関数を使って商品テーブルから「(入力済みデータの区分け)+自身の商品ID」のレコードの商品IDを求めています
これで、自身が同じ区分内の商品かチェックされるという仕組みです

'//(2回目以降の入力)部分
│,┌DLookUp("商品ID"
│ │,"商品テーブル"
│ │,"区分け='" & 
│ │ (入力済みデータの区分け)
│ │  & "' AND 商品ID=" & [商品ID]
│ └)

「(入力済みデータの区分け)」の取得方法はそのまま関数が何をやっているのか考えてみてください。


取引明細テーブルに区分けデータも記録しているのであれば、区分け判定部分の計算式はもう少し単純にできそうですね


実装方法はいくつか考えられますが、「入力規則に計算式を一つ入れればいい」が謳い文句として簡単そうに見えたのでそれで実現しています。(VBA使ったほうがすっきりする回答になるんですけどね)

5

hirotonさん すごく分かり易い表現でして頂きありがとうございます。お手数かけました。
中々すごい式ですね。VBAでする場合はどう記述したらいいでしょうか? サブフォームで2つ目以降の商品をコンボで選んだ時に警告が出てキャンセルが理想的です。そうすると色々と認識しやすいので。
宜しくお願いします。

6
hiroton 2023/06/09 (金) 17:01:22 b43ba@f966d

VBAでやるなら更新前処理でチェックすればいいでしょう

キャンセルが理想的

入力内容はなかったことにして元に戻すならMe.Undoが使えます

Private Sub 商品ID_BeforeUpdate(Cancel As Integer)
    '//最初の登録の時は何もしない
    If Me.Recordset.RecordCount <= 1 Then Exit Sub
    
    Dim 区分け As String
    区分け = DLookup("区分け", "商品テーブル", "商品ID=" & DLookup("商品ID", "取引明細テーブル", "取引ID=" & Me!取引ID & " AND 明細ID <> " & Me!明細ID))

    If Me!商品ID.Column(2) <> 区分け Then
        Me.Undo
        MsgBox "区分けエラー"
    End If
End Sub

入力規則のように入力内容をチェックする必要はないので「区分け」の比較だけでいいですね。入力中のデータの方はコンボボックスのデータに「区分け」も含めるようにしておけば簡単に指定できます

7
hiroton 2023/06/09 (金) 17:30:18 b43ba@f966d

もっと言えば、サブフォームのレコードソースをクエリにして、「区分け」を含むようにすれば更に簡単ですね

「取引明細テーブル」と「商品テーブル」を商品IDで結合して区分けフィールドを追加します
自分のグループで自分以外に同じ区分けがあるか?をチェックすればいいので

Not IsNull(DLookup("区分け","Q取引明細","取引ID="&[取引ID]&" AND 明細ID<>"&[明細ID]&" AND 区分け='"&[区分け]&"'"))

で、同じ「区分け」を指定していると判断できます
入力規則で判定するなら、これを使って

=IIf(DCount("*","取引明細テーブル","取引ID=" & [取引ID])<=1 Or Not IsNull(DLookup("区分け","Q取引明細","取引ID="&[取引ID]&" AND 明細ID<>"&[明細ID]&" AND 区分け='"&[区分け]&"'")),[商品ID])

VBAでやるなら、現在サブフォームに読み込まれているレコード内で比較すればいいので

Private Sub 商品ID_BeforeUpdate(Cancel As Integer)
    '//最初の登録の時は何もしない
    If Me.Recordset.RecordCount <= 1 Then Exit Sub
    
    With Me.RecordsetClone
        If Me.CurrentRecord = 1 Then .Next '//先頭レコード同士で比較しない
        If Me!区分け <> !区分け Then
            Me.Undo
            MsgBox "区分けエラー"
        End If
        .close
    End With
End Sub

このように実装できます

9
hiroton 2023/06/12 (月) 08:48:38 2ba59@f966d >> 7

一部修正

Private Sub 商品ID_BeforeUpdate(Cancel As Integer)
    '//最初の登録の時は何もしない
    If Me.Recordset.RecordCount <= 1 Then Exit Sub

    With Me.Recordset.Clone
        If Me!区分け <> !区分け Then
            Me.Undo
            MsgBox "区分けエラー"
        End If
        .close
    End With
End Sub

Recordset.Cloneを使うと編集前の状態でRecordsetが作成されるようです(考えてみれば当たり前)。最初のレコードの編集でも編集前と編集後を比較できるのでレコード位置のチェックは不要になりました

参考
フォームの Recordset, RecorsetClone, RecordSet.Clone の違いとは?(hatena chipsさん)

8
りんご 2023/06/10 (土) 02:19:06 935bc@0e907

事情がかわり取引毎に同一区分け商品だけが紐付けしなくてはいけなくなった

 取引メインテーブルに区分けフィールドを追加して下さい。
 取引メインフォームに区分けフィールド連結テキストボックスを追加して下さい。
 そして、取引明細サブフォームの商品選択(コンボにて)の抽出条件に取引メインフォームの区分けフィールド連結テキストボックスを設定するような仕組みを考えて下さい。
 例えば、ヘッダーに商品クエリのリストボックスを置く。取引メインフォーム区分け連結テキストボックスの値に応じて、動的に絞り込み、結果が表示されるようにする。必要な商品を選択して、ボタンを押せば、取引明細サブフォームの商品選択に反映されるようにする。

10

色々なアイデアありがとうございました(レスポンス遅くなりました)。
それぞれ試してみたのですが無反応でした。これは私が完全に理解しておらずVBAの間違い・不足によるものではと思ってます。
それで乏しい知識をひねり、対象の取引IDに対するDFirst関数で先頭のものを参照して2つ目以降を比較することで何とか上手くいきました。
もっと勉強してから頂いたアイデアも再トライしたいと思っています。
色々とお手数かけました。