MS Access-これ以上テーブルを開けない
質問
作業時には、いくつかのMS Access mdbファイルを処理する必要があるため、Sun JVMに付属するデフォルトのJdbcOdbcBridgeドライバーを使用します。ほとんどの場合、これはうまく機能します。
問題は、いくつかの大きなファイルを処理する必要があるとき、「これ以上テーブルを開けません」というメッセージで何度か例外に直面することです。どうすればそれを回避できますか?
PreparedStatementsおよびRecordSetsのすべてのインスタンスを既に閉じており、それらの変数をnullに設定している場合でも、この例外は引き続き発生します。私たちは何をすべき?これらの厄介な例外をどのように回避できますか?ここの誰かが方法を知っていますか?
この問題を回避するために変更できるWindowsのODBCドライバーに追加の構成はありますか?
解決
"これ以上テーブルを開けません" 「これ以上データベースを開けません」よりも優れたエラーメッセージです。私の経験ではより一般的です。実際、後者のメッセージはほとんど常に前者を隠しています。
Jet 4データベースエンジンには、2048個のテーブルハンドルの制限があります。これが接続の存続期間内で同時であるか累積的であるかは、私には完全には明らかではありません。実際に一度に開くレコードセットの数を減らすと、問題を回避できるようになるため、これは常に累積的であると想定していました。
問題は、「テーブルハンドル」テーブルハンドルを参照するだけでなく、もっと多くのものを参照します。
このSQLで保存されたQueryDefを検討してください:
SELECT tblInventory.* From tblInventory;
QueryDefを実行すると、2つのテーブルハンドルが使用されます。
なに? 1つのテーブルのみを使用します!ただし、Jetはテーブルにテーブルハンドルを使用し、保存されたQueryDefにテーブルハンドルを使用します。
したがって、次のようなQueryDefがある場合:
SELECT qryInventory.InventoryID, qryAuthor.AuthorName
FROM qryInventory JOIN qryAuthor ON qryInventory.AuthorID = qryAuthor.AuthorID
...各ソースクエリに2つのテーブルが含まれている場合、これらに対応するテーブルハンドルを使用しています:
Table 1 in qryInventory
Table 2 in qryInventory
qryInventory
Table 1 in qryAuthor
Table 2 in qryAuthor
qryAuthor
the top-level QueryDef
したがって、4つのテーブルしか含まれていない(ベーステーブルが4つしかないため)と思われるかもしれませんが、実際には、これら4つのベーステーブルを使用するために7つのテーブルハンドルを使用します。
レコードセット内で、7つのテーブルハンドルを使用する保存済みのQueryDefを使用する場合、さらに別のテーブルハンドルを使い果たし、合計で8つになります。
Jet 3.5の時代に戻って、元のテーブルハンドルの制限は1024でした。作業中のアプリを設計した後、データファイルを複製したときに期限に間に合いました。問題は、レプリケーションテーブルの一部が常に開いていること(おそらく各レコードセットごとに?)であり、アプリを一番上に置くのに十分なだけのテーブルハンドルを使用していたことです。
そのアプリの元のデザインでは、サブフォーム、コンボボックス、リストボックスがたくさんあるヘビーウェイトフォームをたくさん開いていました。多くの場所(サーバーデータベースのビューを使用する場合と同様)。問題を修正したのは:
-
サブフォームが表示されたときにのみロードします。
-
コンボボックスとリストボックスの行ソースを、それらが画面上にあるときにのみロードする。
-
保存されているすべてのQueryDefを削除し、可能な限り生のテーブルに参加するSQLステートメントを使用します。
これにより、計画より1週間遅れてロンドンオフィスにそのアプリを展開できました。 Jet SP2が登場したとき、テーブルハンドルの数が2倍になりました。これは、Jet 4(およびACE)にまだあるものです。
ODBC経由でJavaからJetを使用する場合、重要なポイントは次のようになります。
-
必要に応じて接続を開いたり閉じたりするのではなく、アプリ全体で単一の接続を使用します(これにより、接続に失敗する危険があります)。
-
必要な場合にのみレコードセットを開き、完了したらリソースをクリーンアップして解放します。
今、JDBC => ODBC => Jetチェーンのどこかにメモリリークがあり、リソースをリリースしていると考え、まったくリリースされていない可能性があります。 JDBCに固有のアドバイスはありません(使用しないので、結局Accessプログラマーです)が、VBAではオブジェクトを明示的に閉じてメモリ構造を解放することに注意する必要があります。 VBAは参照カウントを使用し、オブジェクトへの参照が解放されたことを知らない場合があるため、オブジェクトがスコープ外になったときにそのオブジェクトのメモリを解放しません。
したがって、VBAコードでは、これを行うたびに:
Dim db As DAO.Database
Dim rs As DAO.Recordset
Set db = DBEngine(0).OpenDatabase("[database path/name]")
Set rs = db.OpenRecordset("[SQL String]")
...必要なことを完了したら、これを終了する必要があります:
rs.Close ' closes the recordset
Set rs = Nothing ' clears the pointer to the memory formerly used by it
db.Close
Set db = Nothing
...それは、宣言された変数がそのコードの直後にスコープから外れた場合でもです(これはrel
他のヒント
最近、MS Access用の純粋なJava JDBCドライバーであるUCanAccessを試しました。チェックアウト: http://sourceforge.net/projects/ucanaccess/ -Linuxでも動作します;- )必要なライブラリをロードするには、ある程度の時間が必要です。まだ読み取り専用以外の目的でテストしていません。
とにかく、sun.jdbc.odbc.JdbcOdbcDriverで上記のような問題が発生しました。 System.gc()ステートメントだけでなく、ステートメントオブジェクト(およびそれらに対するexecuteUpdateの呼び出し)の作成後にclose()ステートメントを追加した後、エラーメッセージが停止しました;-)
無料のネットワーク接続が不足している可能性があります。仕事中の忙しいシステムでこの問題が発生しました。
注意すべき点は、ネットワーク接続が閉じられていても、ガベージコレクションの時間までソケットが解放されない可能性があることです。これは NETSTAT / A / N / P TCP
で確認できます。 TIME_WAIT
状態の接続が多数ある場合は、接続のクローズまたは定期的な間隔でガベージコレクションを強制してみてください。
Connectionオブジェクトも閉じる必要があります。
jdbc odbcドライバーの代替案も検討することをお勧めします。自分で代替の経験はありませんが、これは始めるのに良い場所です:
同じ問題が発生しましたが、上記のどれも機能していませんでした。私は最終的にこの問題を突き止めました。 これを使用してフォームの値を読み取り、ルックアップリストのレコードソースに戻しました。
LocationCode = [Forms]![Support].[LocationCode].Column(2)
ContactCode = Forms("Support")("TakenFrom")
以下に変更すると動作します。
LocationCode = Forms("Support")("LocationCode")
ContactCode = Forms("Support")("TakenFrom")
もっとうまく書けばよかったのは知っているが、これが同じ状況の誰かに役立つことを願っている。
ありがとう グレッグ