При разработке механизмов импорта данных из внешних файлов (например, Excel) программисты часто сталкиваются с особенностью работы объекта ПостроительЗапроса. Когда мы используем ОписаниеИсточникаДанных для получения таблицы значений из ТабличногоДокумента, система автоматически считает первую строку переданной области заголовком. В результате данные из первой строки превращаются в имена колонок будущей таблицы значений и фактически «теряются» для дальнейшей обработки. Рассмотрим подробнее, почему это происходит и как правильно обойти это ограничение, сохранив все данные в целостности.
Разберем ситуацию: у нас есть объект ТабличныйДокумент, загруженный из файла. Мы хотим быстро превратить его в ТаблицуЗначений. Стандартный путь в 1С для этого — использование ПостроителяЗапроса. Однако механизм ОписаниеИсточникаДанных спроектирован так, что он ожидает увидеть в первой строке именно названия полей. Даже если ваша область начинается с полезных данных, 1С воспримет их как метаданные колонок.
Проанализируем последствия: если в первой ячейке написано «Иванов», то в результирующей таблице значений появится колонка с именем «Иванов», а сама строка с этой фамилией в таблицу не попадет. Если же в первой ячейке число или пустая строка, это может привести к ошибкам именования колонок или непредсказуемой типизации данных.
Самый простой и очевидный метод, который мы рассмотрим, — это программное добавление пустой строки или строки с техническими именами над данными перед началом загрузки. Выясним, как это реализовать с помощью метода ВставитьОбласть.
Посмотрим на пример кода, где мы добавляем пустую строку в самое начало документа:
// Определяем область, которую нужно сдвинуть (вся таблица)
ОбластьДляСдвига = ТабДок.Область(1, 1, 1, ТабДок.ШиринаТаблицы);
// Вставляем пустую область, сдвигая данные вниз
ТабДок.ВставитьОбласть(ОбластьДляСдвига, ОбластьДляСдвига, ТипСмещенияТабличногоДокумента.ПоВертикали);
// Теперь первая строка пустая, и Построитель примет её за заголовок
ИсточникДанных = Новый ОписаниеИсточникаДанных(ТабДок.Область());
Построитель = Новый ПостроительЗапроса;
Построитель.ИсточникДанных = ИсточникДанных;
Построитель.Выполнить();
ТЗ = Построитель.Результат.Выгрузить();
Важный нюанс: при таком подходе индексы всех строк в исходном документе изменятся. Если после загрузки вам нужно продолжать работу с тем же документом, учитывайте, что все данные «съехали» на одну строку вниз.
Проанализируем ситуацию, описанную в сообщениях форума: что делать, если документ содержит несколько именованных областей или страниц, и при вставке строк «таблица плывет»? Выясним причину: когда вы вставляете строку в начало документа внутри цикла, координаты всех последующих областей меняются. Вторая область, которая раньше начиналась с 10-й строки, теперь начинается с 11-й, но ваш цикл об этом «не знает».
Для решения этой проблемы мы применим метод создания временного объекта ТабличныйДокумент. Разберем по шагам, как это работает:
ТабличныйДокумент.Посмотрим на реализацию этого алгоритма:
// Основной ТабДок уже прочитан из файла
ТД_Временный = Новый ТабличныйДокумент;
// Генерируем заголовки колонок во временном документе
Для Сч = 1 По ТабДок.ШиринаТаблицы Цикл
НомерКолонки = Формат(Сч, "ЧГ=0");
ОбластьЗаголовка = ТД_Временный.Область(1, Сч);
ОбластьЗаголовка.Текст = "Колонка_" + НомерКолонки;
КонецЦикла;
// Копируем данные из оригинала во временный документ под созданную шапку
ТД_Временный.Вывести(ТабДок);
// Работаем с временным документом
Построитель = Новый ПостроительЗапроса;
Источник = Новый ОписаниеИсточникаДанных(ТД_Временный.Область(1, 1, ТД_Временный.ВысотаТаблицы, ТД_Временный.ШиринаТаблицы));
Построитель.ИсточникДанных = Источник;
Построитель.ЗаполнитьНастройки();
Построитель.Выполнить();
ТЗ = Построитель.Результат.Выгрузить();
Этот способ является наиболее безопасным, так как он не модифицирует исходные данные и гарантирует корректную работу с именами колонок в ТаблицеЗначений.
Рассмотрим еще один важный аспект: типизация. Когда мы используем ПостроительЗапроса, он пытается автоматически определить типы колонок на основе данных в ТабличномДокументе. Если вы назвали колонку «Колонка_1», а под ней находятся числа, Построитель может решить, что вся колонка имеет тип Число. Однако, если в какой-то строке попадется текст, это может привести к ошибке или потере данных.
Если вам критически важны типы данных, мы рекомендуем после получения ТЗ программно типизировать колонки или вовсе отказаться от Построителя в пользу прямого обхода ячеек ТабличногоДокумента в цикле:
ТЗ = Новый ТаблицаЗначений;
// Создаем колонки вручную
Для Кол = 1 По ТабДок.ШиринаТаблицы Цикл
ТЗ.Колонки.Добавить("Колонка" + Кол);
КонецЦикла;
// Обходим строки и заполняем ТЗ
Для Стр = 1 По ТабДок.ВысотаТаблицы Цикл
НоваяСтрока = ТЗ.Добавить();
Для Кол = 1 По ТабДок.ШиринаТаблицы Цикл
НоваяСтрока[Кол-1] = ТабДок.Область(Стр, Кол).Текст;
КонецЦикла;
КонецЦикла;
Этот метод работает медленнее на огромных объемах данных, но он абсолютно предсказуем и гарантирует чтение каждой строки, включая самую первую, без дополнительных манипуляций со вставкой областей.
В завершение проанализируем ситуацию, когда источником данных является файл Excel. Если ваша задача — просто прочитать файл, минуя визуальное отображение в 1С, стоит рассмотреть технологию ADO (ActiveX Data Objects). В строке подключения к Excel через провайдер Microsoft.ACE.OLEDB можно указать параметр HDR=No. Это прямо скажет системе, что первая строка содержит данные, а не заголовки.
Для современных систем на базе Linux или при работе в облаке, где COM-объекты недоступны, рекомендуется использовать чтение XLSX как XML-архива. Это позволяет напрямую обращаться к содержимому узлов Sheet.xml, где строки пронумерованы от 1 до N, и никакие механизмы построителей не будут навязывать вам логику «заголовков».
Подведем итог: если вы используете ПостроительЗапроса, всегда добавляйте техническую строку сверху. Если вы работаете с многостраничным документом — делайте это во временном объекте ТабличныйДокумент, чтобы не нарушить структуру индексов.