«Не удается открыть больше таблиц» - это лучшее сообщение об ошибке, чем «Не удается открыть больше баз данных», которое чаще встречается в моем опыте. Фактически, последнее сообщение почти всегда маскирует первое.
Ядро базы данных Jet 4 имеет ограничение в 2048 таблиц дескрипторов . Мне не совсем понятно, является ли это одновременным или накопительным в течение жизни соединения. Я всегда предполагал, что это кумулятивно, поскольку на практике открытие меньшего количества наборов записей одновременно позволяет избежать этой проблемы.
Проблема в том, что «дескрипторы таблиц» относятся не только к дескрипторам таблиц, но к чему-то гораздо большему.
Рассмотрим сохраненный QueryDef с этим SQL:
SELECT tblInventory.* From tblInventory;
Запуск этого QueryDef использует ДВА дескриптора таблицы.
Что ?, спросите вы? Используется только одна таблица! Но Jet использует дескриптор таблицы для таблицы и дескриптор таблицы для сохраненного QueryDef.
Таким образом, если у вас есть QueryDef, как это:
SELECT qryInventory.InventoryID, qryAuthor.AuthorName
FROM qryInventory JOIN qryAuthor ON qryInventory.AuthorID = qryAuthor.AuthorID
... если в каждом из ваших исходных запросов есть две таблицы, вы используете эти дескрипторы таблиц, по одному для каждой:
Table 1 in qryInventory
Table 2 in qryInventory
qryInventory
Table 1 in qryAuthor
Table 2 in qryAuthor
qryAuthor
the top-level QueryDef
Итак, вы можете подумать, что задействованы только четыре таблицы (потому что есть только четыре базовые таблицы), но вы на самом деле будете использовать 7 таблиц дескрипторы , чтобы использовать эти 4 базовые таблицы.
Если в наборе записей вы используете сохраненный QueryDef, который использует 7 дескрипторов таблиц, вы использовали еще один дескриптор таблицы, всего 8 *.
Еще в Jet 3.5 дней ограничение исходной обработки таблицы составляло 1024, и я столкнулся с ним в крайний срок, когда я реплицировал файл данных после разработки работающего приложения. Проблема заключалась в том, что некоторые из таблиц репликации были открыты всегда (возможно, для каждого набора записей?), И для этого потребовалось достаточно больше дескрипторов таблиц, чтобы приложение превысило все возможности.
В оригинальном дизайне этого приложения я открывал кучу тяжеловесных форм с множеством подчиненных форм, а также со списками и списками, и в то время я использовал множество сохраненных QueryDef для предварительной сборки стандартных наборов записей, которые я использовал бы в много мест (как и в случае с представлениями на любой базе данных сервера). Что решило проблему было:
загрузка подчиненных форм только при их отображении.
загрузка источников строк полей со списками и списков только тогда, когда они были на экране.
избавление от всех сохраненных QueryDefs и использование операторов SQL, которые соединяли необработанные таблицы, где это возможно.
Это позволило мне развернуть это приложение в лондонском офисе только на одну неделю позже, чем планировалось. Когда вышел Jet SP2, он удвоил количество дескрипторов таблиц, что у нас все еще есть в Jet 4 (и, я полагаю, ACE).
С точки зрения использования Jet из Java через ODBC ключевым моментом, я думаю, будет:
используйте одно соединение во всем приложении, а не открывайте и закрывайте их по мере необходимости (что может привести к невозможности их закрытия).
открывать наборы записей только тогда, когда они вам нужны, очищать и освобождать их ресурсы, когда вы закончите.
Теперь может быть, что где-то есть утечки памяти в цепочке 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
... и даже если объявленные вами переменные выходят из области действия сразу после этого кода (который должен освободить всю используемую ими память, но не делает это на 100% надежно).
Теперь я не говорю, что это то, что вы делаете в Java, но я просто предлагаю, что если у вас возникли проблемы и вы думаете, что высвобождаете все свои ресурсы, возможно, вам нужно определить, Это зависит от сборки мусора, и вместо этого нужно делать это явно.
Простите, если я сказал что-нибудь глупое в отношении Java и JDBC - я просто сообщаю о некоторых проблемах, с которыми сталкиваются разработчики Access при взаимодействии с Jet (через DAO, а не ODBC), которые сообщают об одном сообщение об ошибке, которое вы получаете, в надежде, что наш опыт и практика могут предложить решение для вашей конкретной среды программирования.