Access (VBA)

Access VBAに関するフォーラムです。
  • 掲示板への投稿には会員登録(無料)が必要です。会員登録がまだの方はこちら
  • 掲示板ご利用上のお願い」に反するご記入はご遠慮ください。
  • Q&A掲示板の使い方はこちらをご覧ください
トピックに返信
質問

 
(Windows 10全般 : Access 2016)
検索や更新等の処理後の表示について
投稿日時: 22/11/13 20:09:47
投稿者: おーさん0729

お世話になります。
 
検索や更新等の処理後の表示についてですが、同じボタンを押しても、処理結果が変わります。
 
フォームの全テキストボックスに#Name?と表示されたり、「ブックマークが正しくありません」や「指定した式に、CurrentRecordプロパティに対する正しくない参照が含まれます」のメッセージが出ます。
 
ブレークポイントをつくり、ステップインで1行ずつ実行していくと問題なく実行できることが多いです。
 
なぜ、このようになるかがわからないので、教えていただきたいです。
 
あいまいな質問で申し訳ありませんが、よろしくお願いします。

回答
投稿日時: 22/11/14 09:13:01
投稿者: Suzu

推測 になりますが
 ・Docmd.〜 の命令による レコード処理
 ・DAO/ADO の命令による レコード処理
を混在させているのではありませんか?
 
アクセスするデータは同じですが、アクセスする経路が変わります。
 
データ本体に対する処理を片側の経路(経路:A)で行い
別の経路(経路:B)からその処理を行ったデータを参照しに行った際、
先経路(経路:A)の処理が反映されていない状態が発生し、正確なデータが参照できない状態が発生します。
 
再現できる情報も何もないので、推測でしかありません。

投稿日時: 22/11/14 19:42:09
投稿者: おーさん0729

Suzu さんの引用:
推測 になりますが
 ・Docmd.〜 の命令による レコード処理
 ・DAO/ADO の命令による レコード処理
を混在させているのではありませんか?
 
アクセスするデータは同じですが、アクセスする経路が変わります。
 
データ本体に対する処理を片側の経路(経路:A)で行い
別の経路(経路:B)からその処理を行ったデータを参照しに行った際、
先経路(経路:A)の処理が反映されていない状態が発生し、正確なデータが参照できない状態が発生します。
 
再現できる情報も何もないので、推測でしかありません。

 
たしかに混在しているところもあります。
間にRequeryやRefreshやDo eventsを入れて、更新しているようにしてるのですが、それでもダメなのでしょうか?

回答
投稿日時: 22/11/14 22:06:00
投稿者: hatena
投稿者のウェブサイトに移動

おーさん0729 さんの引用:
フォームの全テキストボックスに#Name?と表示されたり、「ブックマークが正しくありません」や「指定した式に、CurrentRecordプロパティに対する正しくない参照が含まれます」のメッセージが出ます。

 
コードが間違っているのでしょう。
Suzuさんから推測の回答がでてますが、
実際のコードを提示してもらわないと、これ以上回答はむずかしいでしょう。
 
おーさん0729 さんの引用:
間にRequeryやRefreshやDo eventsを入れて、更新しているようにしてるのですが、それでもダメなのでしょうか?

処理によってはそれで解決する場合もありますが、実際にそれではダメだったんですよね。
 
ひとつずつ、ダメだった、コードを提示して質問してみてはどうですか。
 

投稿日時: 22/11/14 22:50:50
投稿者: おーさん0729

@Private Sub Form_Load()
    Set Pcurrentform = Forms!フォーム名
    Set PRecordform = Forms!フォーム名
    Pfname = Me.Name
    Ptname = "テーブル名"
    Printer = Printers("プリンター名")
    Call フォーム開始
End Sub
 
APrivate Sub 全レコード表示_Click()
On Error GoTo err_cmd
    '「表示」にチェックが入ってない方が表示される
    Set Pcurrentform = Screen.ActiveForm
    Ptname = "テーブル名"
    Call 全表示
    DoEvents
    Me.Refresh
    DoEvents
    Exit Sub
err_cmd: MsgBox Error()
End Sub
 
BPublic Sub 全表示()
On Error GoTo err_cmd
    Dim db As Database
    Dim rsa As Recordset
    DoEvents
        Set db = CurrentDb
        Set rsa = Pcurrentform.Recordset
        PCuReF = False
        Pcurrentform.Refresh
        Pstrfilter = ""
        Pcurrentform.OrderBy = ""
        Psort = Pcurrentform.OrderBy
        Pcurrentform.OrderByOn = False
        Pcurrentform.FilterOn = False
        Pcurrentform.反転用.Value = False
    DoEvents
        '対象:テーブル全体のレコードセットをもつ
        PmySQL(1) = "SELECT * FROM " & Ptname & "; "
        Pcurrentform.RecordSource = PmySQL(1)
    DoEvents
        '全レコードの[表示]をfalseにする
        PmySQL(1) = "UPDATE " & Ptname & " SET " & Ptname & ".表示 = false "
        PmySQL(1) = PmySQL(1) & "WHERE (((" & Ptname & ".表示)=True)); "
        db.Execute PmySQL(1)
    DoEvents
        Pcurrentform.OrderBy = "[ならび]"
        Pcurrentform.OrderByOn = True
        Pcurrentform.ソート状況 = "未ソート"
        Pfil = ""
        Psort = ""
    DoEvents
        Pbkstr = "ならび= " & Pbk
        Pcurrentform.Recordset.FindFirst Pbkstr
        PCuReF = True
    DoEvents
    Exit Sub
err_cmd:
End Sub
 
CPrivate Sub Form_Current()
On Error GoTo err_cmd
    Dim db As Database
        Set db = CurrentDb
        Set PRecordform = Forms!フォーム名
    '対象:画面表示されているレコードセットをもつ
    Set Prsf = PRecordform.Recordset
        PRecordform.カレント = PRecordform.CurrentRecord
        DoEvents
        Call フォームカレント
    '変数の解放
    Set db = Nothing
    Set Prsf = Nothing
    Exit Sub
err_cmd: MsgBox Error()
End Sub
 
DPublic Sub フォームカレント()
On Error Resume Next
        'カレントレコードが1の時は前へいけない
        If PRecordform.カレント = 1 Then
            PRecordform.レコード前.Enabled = False
        Else
            PRecordform.レコード前.Enabled = True
        End If
            DoEvents
        'カレントレコードが最大の時は次へいけない
        If PRecordform.カレント = Prsf.RecordCount Then
            PRecordform.レコード次.Enabled = False
        ElseIf Prsf.RecordCount = 0 Then
            PRecordform.レコード次.Enabled = False
        ElseIf Prsf.RecordCount = 1 Then
            PRecordform.レコード次.Enabled = False
        Else
            PRecordform.レコード次.Enabled = True
        End If
            DoEvents
        If PCuReF = True Then
            Pbk = PRecordform.[ならび].Value
        End If
    Exit Sub
err_cmd:
End Sub
 
Public Sub フォーム開始()
On Error Resume Next
            PmySQL(1) = "SELECT * FROM " & Ptname & "; "
            Pcurrentform.RecordSource = PmySQL(1)
        If Pfil = "on" And Not Psort = "" Then 'フィルターとソート
                '"[表示]=true"を表示
                Pcurrentform.Filter = "[表示]=true"
                Pcurrentform.FilterOn = True
                Pcurrentform.反転用.Value = True
             
                Pcurrentform.OrderByOn = False
                Pcurrentform.OrderBy = Psort
                Pcurrentform.OrderByOn = True
            Psort = Pcurrentform.OrderBy
        ElseIf Pfil = "on" And Psort = "" Then 'フィルター
                '"[表示]=true"を表示
                Pcurrentform.Filter = "[表示]=true"
                Pcurrentform.FilterOn = True
                Pcurrentform.反転用.Value = True
            If Pchk = "A表" Then
                Pcurrentform.反転用.Value = False
                Pchk = ""
            Else
            End If
        ElseIf Pfil = "" And Not Psort = "" Then 'ソート
                Pcurrentform.OrderByOn = False
                Pcurrentform.OrderBy = Psort
                Pcurrentform.OrderByOn = True
            Psort = Pcurrentform.OrderBy
        Else
                Call 全表示
        End If
            DoEvents
        If Pcurrentform.OrderByOn = True And Not Pcurrentform.OrderBy = "[ならび]" Then
            Pcurrentform.ソート状況 = "ソート済み"
        Else
            Pcurrentform.ソート状況 = "未ソート"
        End If
        'カレントレコードの持越し
        PCuReF = True
        If PCuReF = True Then
                Pbkstr = "ならび=" & Pbk
                Pcurrentform.Recordset.FindFirst Pbkstr
        End If
        'カレントレコードが1の時は前へいけない
        PRecordform.レコード前.Enabled = False
    Exit Sub
err_cmd:
End Sub
 
該当しているであろう辺りのコードの一例です。ほかのコード箇所でも同じような質問内容のようなことが起こります。
イメージとしては、フォームを開いて@、フォーム上のボタンAを押す(Aの処理が終わるまでにBCを何度か)という流れです。
ここでのしたいことは、レコードの全件表示(フィルター、ソートがかかっていない)です。
「ならび」というフィールドは、フィルター、ソートがかかっていない状態でも、順番が変わらないようにするためです。オートナンバーです。

回答
投稿日時: 22/11/15 11:10:04
投稿者: Suzu

大きな問題としては
 

引用:
'対象:テーブル全体のレコードセットをもつ
  PmySQL(1) = "SELECT * FROM " & Ptname & "; "
  PCurrentform.RecordSource = PmySQL(1)
 
  '全レコードの[表示]をfalseにする
  PmySQL(1) = "UPDATE " & Ptname & " SET " & Ptname & ".表示 = false "
  PmySQL(1) = PmySQL(1) & "WHERE (((" & Ptname & ".表示)=True)); "
 
  db.Execute PmySQL(1)

 
ここ。
 
前半で、フォームのレコードソースを テーブル「Ptname」の全レコードをSELECTする様に変更。
後半で、その テーブル「Ptname」 の データを
   フォーム上にレコードを表示しながら ← レコードを使用している状態
  その表示しているかも知れないレコードに対し、レコード編集
を行っています。
 
好ましい順番としては
 
OnError ResumeNext

'レコードソースを空にする
recSource = PCurrentform.RecordSource
PCurrentform.RecordSource = ""

'レコード更新
db.Execute "UPDATE " & Ptname & " SET 表示 = false "", dbFailOnerror

If Error.Number = 0 Then
  'レコード更新成功
  PCurrentform.RecordSource = Ptname
Else
  'レコード更新失敗
  PCurrentform.RecordSource = recSource
End If

 
の様になるかと。
 
 
直接関係ないですが RecordSouce ではなく Filter で 制御しても良いと思いますよ。

投稿日時: 22/11/15 19:51:44
投稿者: おーさん0729

Suzu さんの引用:
大きな問題としては
 
引用:
'対象:テーブル全体のレコードセットをもつ
  PmySQL(1) = "SELECT * FROM " & Ptname & "; "
  PCurrentform.RecordSource = PmySQL(1)
 
  '全レコードの[表示]をfalseにする
  PmySQL(1) = "UPDATE " & Ptname & " SET " & Ptname & ".表示 = false "
  PmySQL(1) = PmySQL(1) & "WHERE (((" & Ptname & ".表示)=True)); "
 
  db.Execute PmySQL(1)

 
ここ。
 
前半で、フォームのレコードソースを テーブル「Ptname」の全レコードをSELECTする様に変更。
後半で、その テーブル「Ptname」 の データを
   フォーム上にレコードを表示しながら ← レコードを使用している状態
  その表示しているかも知れないレコードに対し、レコード編集
を行っています。
 
好ましい順番としては
 
OnError ResumeNext

'レコードソースを空にする
recSource = PCurrentform.RecordSource
PCurrentform.RecordSource = ""

'レコード更新
db.Execute "UPDATE " & Ptname & " SET 表示 = false "", dbFailOnerror

If Error.Number = 0 Then
  'レコード更新成功
  PCurrentform.RecordSource = Ptname
Else
  'レコード更新失敗
  PCurrentform.RecordSource = recSource
End If

 
の様になるかと。
 
 
直接関係ないですが RecordSouce ではなく Filter で 制御しても良いと思いますよ。

 
記述していただいたところをそっくりそのまま変更してみました。
Cの
   '対象:画面表示されているレコードセットをもつ
    Set Prsf = PRecordform.Recordset
        PRecordform.カレント = PRecordform.CurrentRecord ←
ここで、「指定した式に、CurrentRecordプロパティに対する正しくない参照が含まれます」のメッセージが、
 
'レコード更新
db.Execute "UPDATE " & Ptname & " SET 表示 = false "", dbFailOnerror←
ここで、「オブジェクトが必要です」のメッセージが、
出ました。
 
よろしくお願いします。

回答
投稿日時: 22/11/16 17:06:51
投稿者: Suzu

引用:
記述していただいたところをそっくりそのまま変更してみました。

 
そっくりそのまま動くコードは提示していません。
ここまでのコードを書ける方なので、流れの説明の為に、コードを提示しました。
 
 
フォームのレコードソースに、変数Ptname のテーブル名のSELECT句を指定
 
        '対象:テーブル全体のレコードセットをもつ
        PmySQL(1) = "SELECT * FROM " & Ptname & "; "
        Pcurrentform.RecordSource = PmySQL(1)
 
※1この段階で、フォーム上には、変数Ptname の全レコードが表示されている
 
        '全レコードの[表示]をfalseにする
        PmySQL(1) = "UPDATE " & Ptname & " SET " & Ptname & ".表示 = false "
        PmySQL(1) = PmySQL(1) & "WHERE (((" & Ptname & ".表示)=True)); "
        db.Execute PmySQL(1)
によって
 
変数Ptname の全レコードの 表示フィールドの値 を False に変更している
※1 で表示しているレコードに対しても、値更新を掛けており
※1 の内容と、テーブルに保存されているレコードに齟齬が発生しうる。
 
※2 フォームのレコードロックの設定次第では、
  カレントレコード周辺のレコードについては レコードロックが掛かり
  False への更新が失敗する可能性がある
 
※3 Refresh で画面更新を行い、
   テーブルの変更内容を画面に反映しようとしている。
  でも、初めから更新する事が判っているから、 ※1 の段階の処理は不要
  更新後に、レコードソースを指定した方が効率的
------------------------------------------------------------------------------------------
 
 
提示したコードの解説としては
エラーが発生した場合でも、次の処理を進める
OnError ResumeNext
 
'レコードソースを空にする ※2 の レコードがロックされるのを回避
既存のレコードソース を変数 recSource に保持
recSource = PCurrentform.RecordSource
 
レコードソースを空にする
PCurrentform.RecordSource = ""
 
'レコード更新
変数Ptname の全レコードの 表示フィールドの値 を False に変更している
db.Execute "UPDATE " & Ptname & " SET 表示 = false "", dbFailOnerror
 
エラーが発生しているか判定
If Error.Number = 0 Then
  'レコード更新成功
  レコードソースに、テーブルを指定
 表示されるレコードは、SELECT * FROM 〜と同じなので
 直接テーブルを指定
  PCurrentform.RecordSource = Ptname
Else
 
  'レコード更新失敗
  レコードソースを元に戻す
  PCurrentform.RecordSource = recSource
End If
 
---------------------------------------------------------------
 
上記コードは
 
FileMaker作成物のAccessでの再現について
https://www.moug.net/faq/viewtopic.php?t=81858
 
での
 
引用:
フロント(プログラム)とバック(データベース)に分けて、フロントを使用者パソコンへ、バックを社内NAS(サーバー?)へ入れて、リンクテーブルで使用
を想定しています。
使用人数は最大10人、同時使用は2〜3人を想定しています。

 
については考慮していません。
 
同時使用者が同じ Ptname を 開いている場合、フィールド「表示」の True/False だけで
レコードの表示/非表示を行っては ダメですよね?
 
・開いているユーザー名等も、そのレコードに保存する
・別に表示用のテーブルを用意(ユーザー名、レコードID、表示/非表示の状態)
 :
などの方策が必要でしょう。
 
 
クライアント/サーバー タイプでは、
誰かが レコードを操作している可能性を考慮した仕組みを構築する必要があります。
そういう意味では、全レコードに対し処理 を行う場合は特に注意が必要です。

投稿日時: 22/11/25 20:28:57
投稿者: おーさん0729

[quote="Suzu"]

引用:

 
同時使用者が同じ Ptname を 開いている場合、フィールド「表示」の True/False だけで
レコードの表示/非表示を行っては ダメですよね?
・開いているユーザー名等も、そのレコードに保存する
・別に表示用のテーブルを用意(ユーザー名、レコードID、表示/非表示の状態)
 :
などの方策が必要でしょう。
クライアント/サーバー タイプでは、
誰かが レコードを操作している可能性を考慮した仕組みを構築する必要があります。
そういう意味では、全レコードに対し処理 を行う場合は特に注意が必要です。

 
すみません。遅くなりました。
これを見て、使用者Aが検索かけて、使用者Bが起動すると検索内容を共有(同じ画面になってしまう)することに気づきました。(このことで四苦八苦してました)
使用者Aと使用者Bは違うことをするので、別々の内容が表示される必要があります。
 
やはり、表示用テーブルを作って、データ変更したら、元のテーブルに反映をする
というのが一番ですかね...

トピックに返信