Работа с табличными частями — это одна из самых частых задач, с которыми сталкивается разработчик при создании внешних обработок. На первый взгляд задача кажется простой: нужно «пробежаться» по строкам и забрать данные. Однако на практике начинающие программисты часто натыкаются на ошибки контекста (клиент-сервер) или проблемы с производительностью. В этой статье мы подробно разберем, как правильно обращаться к данным табличной части, почему возникают ошибки типа «Значение не является значением объектного типа» и как оптимизировать ваш код.
Самый очевидный и правильный способ получить значения всех ячеек в определенной колонке — это использование цикла Для каждого. Рассмотрим ситуацию, когда у нас есть внешняя обработка, и в ней создана табличная часть с именем Товары, в которой есть реквизит Цена.
Проанализируем пример кода для обхода такой таблицы:
Для Каждого СтрокаТаблицы Из Объект.Товары Цикл
// Получаем значение из колонки "Цена"
ТекущаяЦена = СтрокаТаблицы.Цена;
// Здесь можно выполнять действия со значением
Сообщить("Цена товара: " + ТекущаяЦена);
КонецЦикла;
В данном примере Объект — это основное представление данных вашей внешней обработки. Переменная СтрокаТаблицы на каждой итерации цикла будет содержать данные текущей строки. Обращение к конкретной ячейке происходит через точку: Строка.ИмяРеквизита.
Часто при попытке выполнить цикл, как показано выше, система выдает ошибку. Выясним причину ее возникновения. Чаще всего это происходит из-за путаницы в контекстах исполнения кода на Клиенте и на Сервере.
В управляемых формах 1С 8.3 данные на форме представлены типом ДанныеФормыКоллекция, в то время как на сервере (в модуле объекта) это полноценная ТабличнаяЧасть. Если вы пытаетесь обратиться к методам, которые доступны только объекту метаданных, находясь в контексте клиентской процедуры, вы получите ошибку.
Рассмотрим важные нюансы:
Объект.ИмяТЧ. Если вам нужно передать эту таблицу в фоновое задание или сложную функцию общего модуля, лучше сначала преобразовать ее в массив структур или таблицу значений на сервере.Иногда полный перебор строк в цикле избыточен. Рассмотрим ситуации, когда нам нужно получить значение конкретной ячейки без обхода всей таблицы.
Если нам заранее известен номер строки, мы можем обратиться к ней напрямую. Помните, что в 1С индексация строк начинается с 0.
// Получаем цену из третьей строки (индекс 2)
Если Объект.Товары.Количество() > 2 Тогда
ЗначениеЯчейки = Объект.Товары[2].Цена;
КонецЕсли;
Если нам нужно найти строку, в которой ячейка содержит определенное значение (например, поиск товара по артикулу), используем встроенный метод:
// Ищем строку, где Артикул равен "001"
СтрокаПоиска = Объект.Товары.Найти("001", "Артикул");
Если СтрокаПоиска <> Неопределено Тогда
// Мы нашли строку, теперь можем взять значение любой другой ячейки в этой строке
НужнаяЦена = СтрокаПоиска.Цена;
КонецЕсли;
Если задача стоит в том, чтобы получить значение ячейки в той строке, которую пользователь выделил курсором прямо сейчас, цикл вообще не нужен. Используем свойство ТекущиеДанные:
// Этот код обычно пишется в модуле формы
ТекущаяСтрока = Элементы.Товары.ТекущиеДанные;
Если ТекущаяСтрока <> Неопределено Тогда
ВыбраннаяЦена = ТекущаяСтрока.Цена;
КонецЕсли;
Разберем ситуацию, упомянутую в обсуждении: когда внутри цикла по табличной части создаются или изменяются объекты базы данных (например, справочники или документы). Рассмотрим пример с копированием объектов:
Для Каждого Строка из Объект.СписокОС Цикл
// Создаем копию объекта из ячейки "ОсновноеСредство"
НовОС = Строка.ОсновноеСредство.Скопировать();
НовОС.Организация = Объект.ОрганизацияПолучатель;
// ВНИМАНИЕ: Запись внутри цикла замедляет работу СУБД
НовОС.Записать();
КонецЦикла;
Важный совет по производительности: Вызов метода Записать() внутри цикла — это «тяжелая» операция. При каждой итерации программа обращается к серверу баз данных, открывает транзакцию, записывает данные и закрывает транзакцию. Если в таблице 1000 строк, это будет работать крайне медленно.
Чтобы ускорить процесс, проанализируем возможность использования транзакций или пакетной записи. Если логика позволяет, лучше сначала собрать все данные в массив, а потом обработать их. Но если запись каждого объекта уникальна и необходима, старайтесь хотя бы минимизировать количество обращений к серверу (используйте серверный контекст процедуры).
Если вам нужно выполнить сложную сортировку, группировку или поиск по нескольким колонкам сразу, мы рекомендуем выгрузить табличную часть в объект ТаблицаЗначений. Это делается одной командой на сервере:
// Работает только в серверном коде
ТаблицаДанных = Объект.Товары.Выгрузить();
// Теперь у нас есть полноценная таблица в памяти, с которой можно работать:
ТаблицаДанных.Сортировать("Цена Убыв");
НужнаяСтрока = ТаблицаДанных.Найти(100, "Цена");
Это особенно полезно во внешних обработках, предназначенных для анализа больших объемов данных, так как работа с ТаблицаЗначений в памяти сервера происходит значительно быстрее, чем прямое обращение к коллекции данных формы.
Для получения значения ячейки во внешней обработке:
Для каждого Строка Из Объект.ИмяТЧ для полного перебора.Элементы.ИмяТЧ.ТекущиеДанные для получения значения из активной строки.