3

テキストの壁に申し訳ありませんが、これは説明が必要で、投稿するにはコードが多すぎます...

データ入力が必要なメソッドのアクセスに固定幅のファイルをインポートしています。transferTextを使用してファイルを2つの仕様にインポートします(1つはグローバル、もう1つは特別な状況です)。

DAOを使用してTableDefs内のすべてのFieldオブジェクトを循環し、AutoIncrement PKを含む複製テーブルを作成する関数があるので、これらのレコードを編集することができます。INSERTINTOを使用してデータをそのテーブルにプッシュします。

よく働く。エラーが見つかった場合、ユーザーはデータ入力に移動して手動でエラーを修正します。これは、400文字の行をふるいにかけ、すべてを想定どおりに再編成するのに勝ります。よく働く!

問題:データ入力が変更されると、フォーム外のモジュール内の関数を呼び出すコミットボタンが押されます。データ入力フォームを閉じて、情報を元のテーブルから自動インクリメントされたPKを差し引いたものにプッシュし、複製されたテーブルをIDでドロップし、エラーをもう一度検索する新しいテーブルを生成することになっています...

元の状態に問題なくプッシュバックしますが、IDテーブルをドロップしません。このテーブルがロックされていることを示すメッセージが常に返されます。すべての関数/サブが終了するまで、テーブルが無期限にロックダウンされていることに気づきました。コードをステップスルーするときはいつでも手動で削除することはできません。実行が終了すると、コードを削除できます。

フォームのコマンドを使用してこれを呼び出したので、すべてのコードが終了し、フォームの終了を呼び出してその処理を実行できるようになるまで、ロックは解放されないと想定しています。何かご意見は?はい、これは非常に野蛮ですが、それは非常にうまく機能します。更新されたコピーを再ドロップできるように、この他のテーブルを惑星から引き裂くことができる必要があります...

最悪の場合、ユーザーにフォームを閉じてメインフォームの別のボタンを押すようにすることができますが、これはユーザーの能力を念頭に置いて設計されています。しかし、これは今私の完全な注意を引いており、それが最適なものでなくても、少なくとも解決策を見つけたいと思っています。

-編集-

この問題では2つの形式が使用されます

FormA (Role: Load in and search for problems)

Examine button is pressed that:

 - Uses TextTransfer based on predefined specs into tempExtract to
       import the file

 - DAO fires off on the Fields collection in tableDefs for
   tempExtract, creates new table tempExtractID

 - Performs searches through the file to find errors.  Errors are saved to
   a table Problem_t.  Table contains Problem_ID (Set from the ID field
   added to tempExtractID) and Description

 - Execution of these tasks is successfully requerying the initial
   form to showing a list of problems and number of occurances.  A button
   gains visibility, with onClick that opens the form DataEntry.            

 - At this point in the code after DAO execution, I can DROP the table
   tempExtractID.  DAO is NOT used again and was only used to build a new table.

FormB-データ入力フォーム

このフォームを開くとすぐに、テーブルtempExtractIDがロックされ、テーブルを削除できなくなります。フォームへのレコードソースは、Problems_tのIDに対してtempExtractIDを照会し、キーイングする必要があるものだけを返します。

フォームが完全に終了するまでテーブルを削除できません。データ入力フォームのボタンを押して変更をコミットします。この場合、ロックエラーが発生する前に起動するコードは5行だけです。

*Xargs refers to the list of Field names pulled earlier through DAO.  As DAO loops through Field objects, the physical names are added to an Xargs String which is placed in this table.  Basically everything but the AutoNumber is being inserted back

    docmd.Close acForm, "frmDataEntry", acSaveNo
    call reInitializeExtract
         > docmd.RunSQL "DELETE FROM tempExtract"
         > docmd.RunSQL "INSERT INTO tempExtract SELECT (" & DLookup("Value", "CONFIG_t", "Item = 'Xargs'" & ") FROM tempExtractID"
    docmd.DeleteObject acTable, "tempExtractID"

これは、フォームが開かれる時間(テーブルが最初にロックされる場所)の間に実行され、すべてのサブと関数が完了するまでロックされ続ける唯一のコードです。

4

2 に答える 2

6

フォームのレコードソースをvbNullStringに設定してから、テーブルを削除することをお勧めします。このテーブルにコンボボックスなどがバインドされていない限り、これは機能するはずです。

于 2009-10-30T11:03:38.960 に答える
4

コードがないと言うのは難しいですが、DAOを使用している場合は、コードオブジェクトをクリーンアップする必要があります。つまり、データベースオブジェクトを[なし]に設定し、レコードセットオブジェクトを閉じて[なし]に設定します。

  Dim db As DAO.Database
  Dim rs As DAO.Recordset

  Set db = DBEngine.OpenDatabase("[path to database]")
  Set rs = db.OpenRecordset("[SELECT statement]")
  rs.Close
  Set rs = Nothing
  db.Execute("[DML or DDL statement]", dbFailOnError)
  db.Close
  Set db = Nothing

  Set db =CurrentDB
  Set rs = db.OpenRecordset("[SELECT statement]")
  rs.Close
  Set rs = Nothing
  Set db = Nothing  ' you don't close a db variable initialized with CurrentDB

VBAは、これらのオブジェクトがスコープ外になったときにクリーンアップすることになっていますが、100%信頼できるわけではありません(VBAは参照カウントを使用してオブジェクトを解放できるかどうかを追跡し、すべての参照がいつ解放されるかを常に把握しているわけではないためです。クリアされました)。

開いたままのオブジェクトがロックの原因である可能性が最も高いため、オブジェクト変数を使い終わったら、オブジェクト変数をクリーンアップしていることを確認する必要があります。

DoCmd.RunSQLを使用していることを確認してから編集してください。

DoCmd.RunSQLを使用すると、問題の原因となる可能性があります。それは確かにあなたの接続のあなたのプログラムによる管理を奪うものです。代わりにDAOを使用すると、接続を制御できるだけでなく、エラーを処理しないというDoCmd.RunSQLの実際の落とし穴を回避できます。DMLまたはDDLステートメントを完全に正常に完了できない場合は、すべてが失敗するはずです。たとえば、100個のレコードを追加していて、そのうちの10個がキー違反で失敗した場合、DoCmd.RunSQLは90個を透過的に追加し、10個の失敗を報告しません。これは、更新やその他のDML/DDLステートメントでも同じです。DoCmd.RunSQLは、可能な限り多くの更新を「便利に」サイレントに完了し、一部の更新が完了しなかったことを知らないままにします。

確かに、場合によっては、それを実現したい場合があります。たとえば、PK衝突があることがわかっているレコードを追加していて、レコードのセットから重複を排除する外部結合にCPUサイクルを費やしたくない場合です。追加しています。

しかし、ほとんどの場合、そうではありません。

上記のコメントで述べたように、DoCmd.RunSQLを透過的に置き換えるように設計された関数を使用し、DAOExecuteステートメントとエラー処理を使用します。SOに数回投稿しました(これが1つです)。現在最もアクティブな開発プロジェクトで本番環境で使用しているバージョンは次のとおりです。

  Public Function SQLRun(strSQL As String, Optional db As Database, _
       Optional lngRecordsAffected As Long) As Long
  On Error GoTo errHandler
    Dim bolCleanup As Boolean

    If db Is Nothing Then
       Set db = CurrentDb
       bolCleanup = True
    End If
    'DBEngine.Workspaces(0).BeginTrans
    db.Execute strSQL, dbFailOnError
    lngRecordsAffected = db.RecordsAffected
    'DBEngine.Workspaces(0).CommitTrans

  exitRoutine:
    If bolCleanup Then
       Set db = Nothing
    End If
    SQLRun = lngRecordsAffected
    'Debug.Print strSQL
    Exit Function

  errHandler:
    MsgBox "There was an error executing your SQL string: " _
       & vbCrLf & vbCrLf & Err.Number & ": " & Err.Description, _
       vbExclamation, "Error in SQLRun()"
    Debug.Print "SQL Error: " & strSQL
    'DBEngine.Workspaces(0).Rollback
    Resume exitRoutine
  End Function

(トランザクションは、トラブルシューティングする時間がなかった問題を引き起こしていたため、コメントアウトされています)

あなたはあなたのこれらの行を置き換えることができます:

  DoCmd.RunSQL "DELETE FROM tempExtract"
  DoCmd.RunSQL "INSERT INTO tempExtract SELECT (" _
    & DLookup("Value", "CONFIG_t", "Item = 'Xargs'" & ") FROM tempExtractID"

...これとともに:

  SQLRun "DELETE FROM tempExtract"
  SQLRun "INSERT INTO tempExtract SELECT (" _
    & DLookup("Value", "CONFIG_t", "Item = 'Xargs'" & ") FROM tempExtractID"

これを行うこともできます:

  Debug.Print SQLRun("DELETE FROM tempExtract") & " records deleted."
  Debug.Print SQLRun("INSERT INTO tempExtract SELECT (" _
    & DLookup("Value", "CONFIG_t", "Item = 'Xargs'" _
    & ") FROM tempExtractID") & " records inserted."

この関数は実行ごとに.RecordsAffectedを返すため、イミディエイトウィンドウに出力するか、戻り値を変数に割り当てるか、既存の変数を変数に渡してその変数を操作します。

  Dim lngRecordsAffected As Long
  ...
  Call SQLRun("DELETE FROM tempExtract", , lngRecordsAffected)
  Debug.Print lngRecordsAffected & " records deleted."
  Call SQLRun("INSERT INTO tempExtract SELECT (" _
    & DLookup("Value", "CONFIG_t", "Item = 'Xargs'" _
    & ") FROM tempExtractID", , lngRecordsAffected)
  Debug.Print lngRecordsAffected & " records inserted."

重要なのは、Executeステートメントにエラーがあると、すべてが失敗するということです(そして、エラーメッセージがポップアップします。エラーが発生した場合、ポップする代わりに-1などを返すように変更することをお勧めします。 MsgBox)。

この関数は、事前にキャッシュされたデータベース変数を渡すことで最も頻繁に使用するため、後でクリーンアップしたくありません。CurrentDB()以外の別のデータベースを使用している場合は、外部データベースを指すデータベース変数がすべて閉じられ、Nothingに設定されていることを確認する必要があります。それがないと、最上位のデータベースオブジェクトでロックが維持され、LDBファイルは開いたままアクティブなままになります。

于 2009-10-30T03:06:35.243 に答える