Как в 1С 7.7 при использовании 1С++ получить результат запроса в миллион строк порциями?

Программист 1С v7.7 IT и автоматизация бизнеса
← На главную

При работе с большими объемами данных в устаревшей, но все еще актуальной платформе 1С:Предприятие 7.7 программисты часто сталкиваются с аппаратными и программными ограничениями. Одной из самых острых проблем является обработка выборок, содержащих миллион и более строк. Стандартная «Таблица Значений» (ТЗ) в 1С 7.7 начинает «валить» программу уже на 400 000 строк из-за нехватки оперативной памяти, так как 32-битное приложение не может адресовать более 2-3 ГБ ОЗУ. В этой статье мы разберем, как правильно организовать порционное получение данных из SQL, используя возможности библиотеки 1С++.

Причина ошибки Connection is busy with results for another hstmt

Прежде чем перейти к решениям, разберем типичную техническую проблему. При попытке обойти результат запроса через Recordset и одновременном получении ссылок на объекты 1С (типизации), часто возникает ошибка: "1C++: Connection is busy with results for another hstmt".

Проанализируем ситуацию: это происходит потому, что 1С пытается выполнить дополнительные подзапросы к базе данных (например, для получения представления ссылки или обращения к реквизиту через точку), пока основное соединение все еще занято открытым курсором SQL-запроса. Для решения этой проблемы мы можем пойти двумя путями: либо выгружать данные во временную структуру, либо использовать отдельное соединение для основного запроса.

Решение 1: Использование временных таблиц и порционное чтение

Наиболее надежный способ обработки миллионных выборок — это перенос результата основного запроса во временную таблицу на стороне SQL-сервера с добавлением уникального индекса (identity). Это позволяет нам «нарезать» результат на любые удобные порции (например, по 50 000 строк) и обрабатывать их последовательно.

Рассмотрим алгоритм реализации по шагам:

  1. Создаем временную таблицу в SQL, структура которой совпадает с вашим запросом, и добавляем колонку ROW_ID INT IDENTITY.
  2. Заполняем эту таблицу результатом вашего основного тяжелого запроса (используя INSERT INTO ... SELECT).
  3. Выбираем данные порциями, используя условие WHERE ROW_ID BETWEEN ... AND ... или SELECT TOP 50000 ... WHERE ROW_ID > @LastID.

Пример логики запроса для получения порции данных:


// Пример SQL-запроса для получения следующей порции
ТекстЗапроса = "
|SELECT TOP 50000 *
|FROM #TempResults
|WHERE ROW_ID > " + ПоследнийИД + "
|ORDER BY ROW_ID";

Такой подход гарантирует, что в оперативной памяти 1С одновременно находится только небольшой объем данных, что предотвращает аварийное завершение программы.

Решение 2: Использование Индексированной Таблицы (ИТЗ)

Если выгрузка во временную таблицу SQL по каким-то причинам неудобна, можно использовать объект ИндексированнаяТаблица из состава 1С++. В отличие от стандартной ТЗ, ИТЗ лучше оптимизирована по памяти и предоставляет более гибкие механизмы работы.

Посмотрим на пример выгрузки результата запроса напрямую в ИндексированнаяТаблица:


ИТЗ = СоздатьОбъект("ИндексированнаяТаблица");
// Метод ВыполнитьИнструкцию может принимать ИТЗ в качестве параметра для выгрузки
ОДБК = СоздатьОбъект("ODBCRecordset");
ОДБК.ВыполнитьИнструкцию(ТекстЗапроса, ИТЗ);

Важный момент: хотя ИТЗ работает быстрее, она все равно потребляет память процесса. Для миллиона строк со ссылками даже ИТЗ может не хватить. Если вы получили ошибку "Error: Cannot allocate memory", значит, объем данных превысил возможности 32-битной архитектуры, и вам необходимо вернуться к порционному чтению (Решение 1).

Решение 3: Визуализация данных через Табличное Поле

Если миллион строк нужно не просто обработать программно, а показать пользователю, использование обычных методов вывода приведет к зависанию интерфейса. В 1С++ для этих целей предусмотрен объект ТабличноеПоле, который работает по принципу виртуального списка (подгружает только те данные, которые видны на экране).

Разберем, как связать результат запроса с экранной формой через ПоставщикДанныхТЗ:


// Процедура инициализации на форме
_Форма = СоздатьОбъект("Форма");
_Форма.УстановитьФорму(Форма);

// Создаем элемент управления на форме (нужно заранее разместить кнопку или текст-пустышку)
ТП = _Форма.СоздатьЭлементУправления("ТабличноеПоле", "Результаты");
ТП.ПоставщикДанных = СоздатьОбъект("ПоставщикДанныхТЗ");

// Создаем ИТЗ и наполняем её данными
ТЗ_Данные = СоздатьОбъект("ИндексированнаяТаблица");
// ... код заполнения ТЗ_Данные из SQL ...

// Привязываем таблицу к поставщику данных
ТП.ПоставщикДанных.ТаблицаЗначений = ТЗ_Данные;

Это позволяет пользователю плавно прокручивать даже очень большие списки без существенной нагрузки на систему.

Дополнительные рекомендации по оптимизации

Проанализируем еще несколько приемов, которые помогут при работе с критическими объемами данных:

  1. Отложенная типизация: Не преобразуйте ID объектов в ссылки прямо в SQL-запросе, если планируете только перекладывать данные. Преобразуйте их в объекты 1С непосредственно перед выполнением действия (например, перед созданием документа).
  2. Использование ROW_NUMBER(): Если вы работаете с современными версиями MS SQL Server, используйте оконную функцию ROW_NUMBER() OVER (ORDER BY ...) для организации пагинации на стороне сервера без создания физических временных таблиц.
  3. Отдельное соединение: Чтобы избежать ошибки Connection is busy, создайте для выполнения запроса отдельный объект ODBCConnection и передайте его в ODBCRecordset методом УстановитьСвязь(). Это позволит 1С выполнять служебные запросы через основное соединение, не конфликтуя с вашим курсором.
  4. Прямая запись в SQL: Если ваша задача — создать на основе выборки корректирующие документы, рассмотрите возможность прямой записи данных в таблицы базы данных SQL ([dt] и [dh] таблицы), минуя объектную модель 1С. Это на порядок быстрее и не требует памяти для объектов 1С, но требует глубокого понимания структуры таблиц и генерации уникальных ID.

Таким образом, комбинируя порционное чтение через временные таблицы и использование оптимизированных объектов библиотеки 1С++, можно эффективно решать задачи по обработке данных любого объема в среде 1С 7.7.

← На главную