При работе на устаревших платформах, таких как 1С:Предприятие 8.1, разработчики часто сталкиваются с серьезной проблемой: штатные средства сохранения табличных документов не поддерживают современный формат .xlsx, а старый формат .xls (BIFF8) ограничен 65 536 строками. Когда возникает задача выгрузить реестр объемом в миллион строк, стандартный метод ТабДок.Записать() становится бесполезным, а обычный построчный перебор через COM-соединение работает крайне медленно, занимая часы времени.
В этой статье мы подробно разберем, как обойти эти ограничения и добиться феноменальной скорости выгрузки данных, сопоставимой с нативными методами современных платформ. Мы проанализируем использование COMSafeArray, обсудим оптимизацию работы с памятью и рассмотрим альтернативные способы записи файлов.
Основная причина медленной работы обычного COM-соединения заключается в том, что каждое обращение к ячейке Excel через Лист.Cells(x, y).Value = Значение — это отдельный вызов между процессами операционной системы. Если у вас миллион строк и 10 колонок, программа должна выполнить 10 миллионов таких вызовов. Это создает огромные накладные расходы.
Рассмотрим основной алгоритм ускорения:
COMSafeArray.Проанализируем пример кода, который реализует этот подход:
// 1. Создаем матрицу данных (Массив массивов или многомерный массив)
// Допустим, у нас есть КоличествоСтрок и КоличествоКолонок
Матрица = Новый Массив(КоличествоСтрок, КоличествоКолонок);
// Заполняем матрицу данными из выборки или ТЗ
Для Сч = 0 По КоличествоСтрок - 1 Цикл
// Заполнение ячеек
Матрица[Сч][0] = Значение1;
Матрица[Сч][1] = Значение2;
КонецЦикла;
// 2. Создаем COMSafeArray
// VT_VARIANT = 12 (универсальный тип данных для Excel)
БезопасныйМассив = Новый COMSafeArray(Матрица, "VT_VARIANT", КоличествоСтрок, КоличествоКолонок);
// 3. Подключаемся к Excel и выгружаем данные разом
Excel = Новый COMОбъект("Excel.Application");
Книга = Excel.Workbooks.Add();
Лист = Книга.Worksheets(1);
// Определяем область (Range), равную размеру нашего массива
НачальнаяЯчейка = Лист.Cells(1, 1);
КонечнаяЯчейка = Лист.Cells(КоличествоСтрок, КоличествоКолонок);
Область = Лист.Range(НачальнаяЯчейка, КонечнаяЯчейка);
// Магия: передаем массив одной строкой кода
Область.Value = БезопасныйМассив;
Как показывает практика, заполнение листа таким способом выполняется практически моментально. Основное время будет тратиться на подготовку массива внутри 1С, но это все равно в десятки раз быстрее, чем работа с каждой ячейкой по отдельности.
При выгрузке миллиона записей в 1С 8.1 мы можем столкнуться с ошибкой «Недостаточно памяти». Это связано с тем, что объект ТаблицаЗначений потребляет значительно больше оперативной памяти, чем обычный Массив.
Разберем по шагам, как минимизировать потребление ресурсов:
ВыборкуИзРезультатаЗапроса и сразу заполнять массив, минуя метод Выгрузить() в таблицу значений.Неопределено.Range в Excel.Если на компьютере, где выполняется выгрузка, не установлен Microsoft Excel, или вы хотите еще больше увеличить скорость, стоит рассмотреть формат XML Spreadsheet 2003 (SpreadsheetML). Это текстовый XML-файл, который Excel открывает как родную таблицу.
Выясним преимущества этого метода: 1С 8.1 очень быстро работает с объектом ЗаписьТекста. Вы можете просто записывать XML-теги строк <Row> и ячеек <Cell> в текстовый файл с расширением .xls. В отличие от бинарного .xls, этот формат не имеет жесткого ограничения в 64к строк (в новых версиях Excel он откроет все данные), и он формируется в разы быстрее любого COM-объекта.
Пример структуры для записи через ЗаписьТекста:
Текст = Новый ЗаписьТекста(ИмяФайла, КодировкаТекста.UTF8);
Текст.ЗаписатьСтроку("<?xml version=""1.0""?>");
Текст.ЗаписатьСтроку("<Workbook xmlns=""urn:schemas-microsoft-com:office:spreadsheet""");
// ... далее заголовки и структура листа ...
Текст.ЗаписатьСтроку("<Table>");
Пока Выборка.Следующий() Цикл
Текст.ЗаписатьСтроку("<Row>");
Текст.ЗаписатьСтроку("<Cell><Data ss:Type=""String"">" + Выборка.Значение + "</Data></Cell>");
Текст.ЗаписатьСтроку("</Row>");
КонецЦикла;
Текст.ЗаписатьСтроку("</Table></Workbook>");
Текст.Закрыть();
Еще один профессиональный подход — работа с файлом Excel как с базой данных через ADO. Этот метод позволяет выполнять SQL-команды INSERT INTO прямо в файл.
Проанализируем ситуацию: этот способ работает очень быстро и потребляет минимум памяти, так как данные передаются через драйвер OLEDB. Однако он требует наличия установленного драйвера Microsoft.ACE.OLEDB на сервере или клиенте. Это отличное решение для фоновых заданий, так как оно не запускает графический процесс excel.exe.
Для достижения максимального результата придерживайтесь следующих правил:
Область.Value2. Оно работает быстрее, так как не пытается интерпретировать формат данных (даты/валюты) в процессе записи.Excel.ScreenUpdating = Ложь и Excel.Calculation = -4135 (ручной расчет формул). Это сэкономит массу ресурсов процессора.Excel.Quit() и обнуляйте переменные, иначе процессы excel.exe будут «висеть» в памяти и потреблять ресурсы.Таким образом, комбинируя использование COMSafeArray с грамотной подготовкой данных в памяти, вы сможете успешно решать задачи по выгрузке сверхбольших объемов данных даже на таких почтенных платформах, как 1С 8.1.