電子書籍の厳選無料作品が豊富!

Access2003での排他制御

顧客管理をAccess2003で行っています。
新規で登録するときに顧客IDの重複チェックを[顧客ID_LostFocus]内で
DCountを使って行っています。

[cmd登録_Click]で
Set db = CurrentDb
Set rst = db.OpenRecordset("顧客情報")
With rst
.AddNew
![顧客id] = me.txt顧客ID.value
![顧客住所] = me.txt顧客住所.value
![顧客TEL] = me.txt顧客TEL.value



.Update
End With

以上の処理を行っています。

(1)Aさんが新規登録で顧客IDを入力
(2)重複されていないので顧客名や住所の入力をおこなう。
(3)Bさんが同じ顧客IDで新規登録

このケースの場合、

Aさんはまだ登録処理が済んでいない(登録ボタンを押していないためテーブルに反映されていない)
ので(3)ではBさんの顧客IDは重複されていません。
Aさんが入力中の顧客IDをBさんが入力した時に「他のユーザーが使用しています」みたいな
メッセージを表示するにはどのようにしたらよろしいでしょうか??

文章が上手く書けなくてもうしわけありません、
何卒宜しくお願いいたします。

A 回答 (4件)

直接的な回答にならないと思います(バフッとした感じで)



テーブル「T2」に、「番号」「名前」「登録日」があったとして、
長整数の「番号」を、新規登録のたびに+1していく例になります。
(エラー処理等、はしょっています)
(Updateでのみエラーが発生するものとして記述しています)
(番号の重複のみが引っかかる場合になります)

「番号」には重複なしのインデックスが設定されているものとします。

新規登録時、操作が競合しないように、「番号」を -999999 で一度登録します。
重複のエラーになったら時間をおいてリトライします。

登録成功で、「番号」最大値+1を覚えておいて、-999999 を書き換えます。
(登録成功で新規登録を抑止できるので、チェックをしたいのであればこのタイミングで)


Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)

Private Function AddRec() As Boolean
  Dim db As DAO.Database
  Dim rs As DAO.Recordset
  Dim iPhase As Integer
  Dim sSql(1) As String
  Dim iErrCnt As Integer
  Dim bErrFound As Boolean
  Dim iTmp As Long
  Const iNumFix As Long = -999999
  Const iRetryCount As Integer = 3

  Set db = CurrentDb
  sSql(0) = "SELECT TOP 1 * FROM T2 ORDER BY 番号 DESC;"
  sSql(1) = "SELECT * FROM T2 WHERE 番号 = " & iNumFix & ";"

  On Error GoTo ERR_HND

  iErrCnt = 0
  iPhase = 0
  Do While (iErrCnt < iRetryCount)
    bErrFound = False
    Set rs = db.OpenRecordset(sSql(iPhase))
    Select Case iPhase
      Case 0
        rs.AddNew
        rs("番号") = iNumFix
        rs("名前") = "AAAABCD"
        rs("登録日") = Date
        rs.Update
        If (Not bErrFound) Then
          rs.Requery
          If (rs("番号") = iNumFix) Then
            iTmp = 1
          Else
            iTmp = rs("番号") + 1
          End If
          iErrCnt = 0
        End If
      Case 1
        rs.Edit
        rs("番号") = iTmp
        rs.Update
    End Select
    rs.Close
    Set rs = Nothing

    If (bErrFound) Then
      Sleep 100
    Else
      iPhase = iPhase + 1
      If (iPhase > 1) Then Exit Do
    End If
  Loop

  Set db = Nothing
  AddRec = iErrCnt < iRetryCount
  Exit Function

ERR_HND:
  rs.CancelUpdate
  iErrCnt = iErrCnt + 1
  bErrFound = True
  iTmp = iTmp + 1
  Resume Next
End Function

フォームから設定値を持って来る、
登録に成功した値をフォームに戻す等は、応用の範囲と思います。

「番号」-999999 が(エラーやトラブルで)残ったままへの処理は、
別途 -999999 を削除するものを作成するとか
レコードに登録した日時フィールドを追加して時間差を見て削除する処理を盛り込むとか・・・
(この場合は各PCの時刻合わせが必要と思います)

「番号」を採番する規則がなければ、この方法では?

この回答への補足

ご丁寧な回答ありがとうございます。
顧客IDは規則性はないです。

このロジックなら上手くいきそうです!

そもそも今の自分の作り方が正しいのかよくわかりません・・・。

補足日時:2010/07/03 15:19
    • good
    • 0

No2です。



エラー処理ですが、AddNewの前にOn Error GoToを使用してエラー時にエラー処理ルーチンに飛ばしたらどうかということです。エラー処理ルーチン内ではErrオブジェクトを利用してエラーの種類を判別して対応するメッセージを表示するか、もしくはErr.Descriptionでエラーメッセージを表示されたらどうかということです。
これでエラーメッセージ終了後に画面に制御を戻すという感じです。
(結局、どこでチェックをしようと、チェック開始前から関連するテーブル全体をロックして他からの更新を排除して処理を行うようなことでもない限り、チェック後にAddNewが完了するまでの間に他から更新される可能性が否定できません。ですのでこのように実行時エラーに備えます。)
    • good
    • 0

私としては仕様変更をお薦めします。



このプログラムはフォームのあるMDBとデータ(テーブル)のMDBを分離して作成し、非連結フォームで入力して更新時にテーブルに登録するタイプのプログラムではないかと想像しています。
この場合にAさん側で起動しているプログラムととBさん側で起動しているプログラムとでやりとりを行えるのはテーブルを介してしか手段がありません。

こうなりますと、Aさんが画面で入力中(まだ顧客情報テーブルには未登録)の顧客IDをBさん側で把握するためには、データのMDBに画面で入力中の顧客IDを一時保存するテーブルを作成し、Aさんが顧客IDを入力した際に(チェックが通った後に)その一時保存用テーブルに入力中の顧客IDを記録するような処理が必要になります。またこれに伴い少なくとも下記の処理が必要です。
・顧客IDのチェックは「顧客情報」テーブル+上記の一時保存用テーブルの両方に対して行う。
・画面で顧客IDを新しく入力した際には、一時保存用テーブルに顧客IDを登録する。
・画面で顧客IDを修正した際には、一時保存用テーブルに登録されている顧客IDも修正する。
・画面で更新したもしくは更新せず画面を終了した際には、一時保存用テーブルの顧客IDを削除する。

しかも、これだけやっても厳密に考えると一時保存用テーブルへの登録時に重複する可能性もありえるわけで・・・

こう考えますと、下記の仕様にした方がいいのではと思います。
・更新時にもう一度チェックを行う。(この場合LostFocusでのチェックは不要かもしれません)
・それでもタイミングが一致してテーブル登録時に重複する可能性があるので、AddNewする際にエラー処理を行う。(重複エラー時にその旨をメッセージ表示し、修正できるように画面に制御を戻す。)

この回答への補足

お丁寧な説明ありがとうございます。

>フォームのあるMDBとデータ(テーブル)のMDBを分離して作成し、
>非連結フォームで入力して更新時にテーブルに登録するタイプのプログラムではないかと
>想像しています。

その通りです。

LostFocusでは重複チェックは行わないで更新時にチェックを行うことにしまう。

>AddNewする際にエラー処理を行う

これは、
if CheckId(me.txt顧客ID.Value) = true then ←CheckId:Dcountを行っている関数
msgbox("顧客IDが重複しています")
else
rst.AddNew



endif

ということでしょうか??

補足日時:2010/07/03 10:20
    • good
    • 0

たぶん以下の要件が必要になるのでしょうから、


これらの要件で検索をしてみてください。

(1)
排他制御とトランザクション処理を行なう。
(2)
その時のロックの競合を回避する。
(3)
ロックしたPCあるいはユーザーを確認、取得


例として
http://www.accessclub.jp/bbs3/0092/superbeg33086 …
    • good
    • 0
この回答へのお礼

簡単そうで複雑なロジックになりそうなので
別の方法を考えます。

また教えていただければ幸いです。
ありがとうございます。

お礼日時:2010/07/03 10:21

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!