Разработка и Конфигурация · 10 мин
Как сделать отбор в табличной части 1С
Как сделать отбор в табличной части 1С: методы НайтиСтроки, Найти, запросы и отбор на форме. Примеры кода, типичные ошибки и ответы на вопросы.
Табличная часть — это коллекция строк документа или справочника, с которой разработчику приходится работать постоянно: найти строки с определённым товаром, удалить лишние позиции, посчитать итог по группе. В 1С для этого предусмотрено несколько механизмов, и выбор между ними влияет на скорость выполнения кода и читаемость программы.
Самый частый сценарий — программный отбор строк по значению одного или нескольких реквизитов. Для этого используется метод НайтиСтроки(), который принимает структуру с параметрами поиска и возвращает массив подходящих строк. Реже применяются поиск первой строки, перебор с условием в цикле и выборка через запрос, когда табличная часть хранится в базе данных.
Важно различать отбор в объекте (табличной части документа в памяти), отбор в динамическом списке на форме и отбор в таблице значений. Механизмы похожи, но синтаксис и ограничения различаются. Ниже разобраны все четыре подхода с примерами кода и указанием, когда какой вариант предпочтительнее.
Отбор строк методом НайтиСтроки() — основной способ
Метод НайтиСтроки() вызывается у табличной части объекта и принимает один параметр — структуру, где ключи совпадают с именами реквизитов табличной части, а значения задают критерий поиска. Результатом будет массив строк, удовлетворяющих всем условиям одновременно (условия объединяются по «И»).
СтруктураОтбора = Новый Структура;
СтруктураОтбора.Вставить("Номенклатура", ТекущаяНоменклатура);
СтруктураОтбора.Вставить("Отменено", Ложь);
НайденныеСтроки = Объект.Товары.НайтиСтроки(СтруктураОтбора);
Для Каждого СтрокаТовара Из НайденныеСтроки Цикл
СтрокаТовара.Сумма = СтрокаТовара.Количество * СтрокаТовара.Цена;
КонецЦикла;
Если строки не найдены, метод вернёт пустой массив, а не Неопределено — это позволяет сразу обходить результат циклом без дополнительной проверки. Однако перед обращением к конкретной строке по индексу проверку количества элементов выполнить нужно:
Если НайденныеСтроки.Количество() > 0 Тогда
ПерваяСтрока = НайденныеСтроки[0];
КонецЕсли;
Как найти одну строку через Найти() и проверить результат
Когда достаточно первой подходящей строки — например, чтобы проверить, есть ли товар в документе, — используется метод Найти(). Он принимает два параметра: искомое значение и имя реквизита, по которому выполняется поиск. Если имя реквизита не указано, поиск идёт по всем колонкам, что замедляет работу и не рекомендуется.
СтрокаТовара = Объект.Товары.Найти(ТекущаяНоменклатура, "Номенклатура");
Если СтрокаТовара = Неопределено Тогда
НоваяСтрока = Объект.Товары.Добавить();
НоваяСтрока.Номенклатура = ТекущаяНоменклатура;
Иначе
СтрокаТовара.Количество = СтрокаТовара.Количество + 1;
КонецЕсли;
Ключевое отличие от НайтиСтроки(): при отсутствии результата Найти() возвращает Неопределено. Пропуск проверки на Неопределено — одна из самых частых причин ошибки «Поле объекта не обнаружено», когда код пытается обратиться к реквизиту несуществующей строки.
Отбор с нестрогими условиями через перебор в цикле
Метод НайтиСтроки() поддерживает только равенство, поэтому условия вида «количество больше нуля», «сумма меньше лимита» или «наименование содержит фрагмент» реализуются обычным перебором с проверкой внутри цикла.
Для Каждого СтрокаТовара Из Объект.Товары Цикл
Если СтрокаТовара.Количество > 0
И СтрокаТовара.Сумма < ЛимитСуммы Тогда
// обработка строки
КонецЕсли;
КонецЦикла;
Для поиска по подстроке используется функция СтрНайти(), при этом регистр имеет значение — для поиска без учёта регистра обе строки приводят к одному регистру через НРег() или ВРег():
Если СтрНайти(НРег(СтрокаТовара.Комментарий), НРег(ИскомыйФрагмент)) > 0 Тогда
// строка подходит
КонецЕсли;
Перебор удобен и тогда, когда найденные строки нужно сразу удалить. Удалять строки безопасно, двигаясь по коллекции с конца, либо собрав их в отдельный массив через НайтиСтроки() и удаляя уже по массиву — тогда сдвиг индексов не мешает обходу.
Отбор через запрос, когда табличная часть в базе данных
Если нужно выбрать строки табличных частей сразу многих документов из базы — например, все позиции с определённым товаром за период, — перебор объектов в памяти будет медленным. Правильный инструмент здесь — запрос к табличной части, имя которой строится как «Документ.ИмяДокумента.ИмяТабличнойЧасти».
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Товары.Ссылка КАК Документ,
| Товары.Номенклатура,
| СУММА(Товары.Количество) КАК Количество
|ИЗ
| Документ.РеализацияТоваровУслуг.Товары КАК Товары
|ГДЕ
| Товары.Номенклатура = &Номенклатура
| И Товары.Ссылка.Дата МЕЖДУ &НачалоПериода И &КонецПериода
|СГРУППИРОВАТЬ ПО
| Товары.Ссылка, Товары.Номенклатура";
Запрос.УстановитьПараметр("Номенклатура", ТекущаяНоменклатура);
Запрос.УстановитьПараметр("НачалоПериода", НачалоПериода);
Запрос.УстановитьПараметр("КонецПериода", КонецПериода);
Результат = Запрос.Выполнить().Выбрать();
Запрос незаменим для агрегации (суммы, количества, группировки) и соединений с другими таблицами. Но он возвращает копию данных, а не живые строки объекта: изменить документ через результат запроса нельзя, для записи всё равно потребуется получить объект.
Сравнение вариантов
Подходит для отбора по равенству одного или нескольких реквизитов в объекте или таблице значений. Ограничение — только строгое равенство
Оптимален для поиска одной строки по одному реквизиту. Требует проверки результата на Неопределено
Единственный вариант для условий «больше/меньше/содержит». Медленнее на больших коллекциях, но гибче
Нужен для выборки по данным многих документов в базе и агрегации. Не позволяет изменять строки напрямую
Отбор строк табличной части на форме для пользователя
Иногда требуется не обработать строки в коде, а показать пользователю на форме только часть строк — например, в большой табличной части отобразить только позиции выбранного склада. Для этого у элемента формы, связанного с табличной частью, устанавливается отбор через свойство ОтборСтрок:
Элементы.Товары.ОтборСтрок = Новый ФиксированнаяСтруктура("Склад", ВыбранныйСклад);
Чтобы снять отбор, свойству присваивается Неопределено. Учтите два ограничения: отбор работает только на равенство, и добавление новой строки при включённом отборе может запутать пользователя — новая строка, не подходящая под условие, сразу скрывается из виду. Поэтому перед интерактивным добавлением отбор часто временно снимают.
- 1Определите реквизит табличной части, по которому будет идти отбор
- 2Создайте фиксированную структуру: Новый ФиксированнаяСтруктура("ИмяРеквизита", Значение)
- 3Присвойте её свойству Элементы.ИмяТаблицы.ОтборСтрок
- 4Проверьте, что в списке остались только нужные строки
- 5При сбросе отбора присвойте ОтборСтрок = Неопределено
Проверка результата и производительность
После выполнения отбора результат стоит проверить тремя способами: количеством найденных строк, содержимым первой строки и поведением кода при пустом результате. Пустой массив от НайтиСтроки() — нормальная ситуация, а вот Неопределено от Найти() требует обязательной ветки обработки.
По производительности: НайтиСтроки() и Найти() выполняют линейный перебор, поэтому многократный поиск в большой табличной части (тысячи строк) внутри цикла заметно тормозит проведение документа. В таких случаях табличную часть выгружают в таблицу значений с индексом (ТЗ.Индексы.Добавить("Номенклатура")) или сворачивают заранее методом Свернуть().
Как проверить результат
Массив с нужным количеством строк, обход циклом работает
Обращение к элементу [0] при пустом массиве вызывает ошибку
Возвращена строка, реквизиты доступны
Возвращено Неопределено, а код без проверки падает
Пользователь видит только подходящие строки
Новые строки исчезают сразу после добавления
Поиск выполняется один раз или по индексированной таблице
НайтиСтроки вызывается внутри цикла по тысячам строк
Часто задаваемые вопросы
Можно ли в НайтиСтроки() задать условие «ИЛИ»?
Напрямую нет: все условия в структуре объединяются по «И». Для «ИЛИ» выполните два вызова НайтиСтроки() с разными структурами и объедините массивы, либо используйте перебор в цикле с оператором ИЛИ в условии.
Как удалить все отобранные строки?
Получите массив через НайтиСтроки(), затем в цикле по массиву вызывайте Объект.Товары.Удалить(СтрокаМассива). Удалять по массиву безопасно, потому что массив не связан с индексами коллекции.
Почему НайтиСтроки() не находит строку, хотя значение совпадает?
Чаще всего причина в несовпадении типов (строка вместо ссылки), лишних пробелах в строковом реквизите или поиске по представлению вместо значения. Проверьте тип и значение реквизита в отладчике.
Работает ли НайтиСтроки() у таблицы значений?
Да, у таблицы значений есть одноимённый метод с тем же синтаксисом. Дополнительно таблицу значений можно проиндексировать, что ускоряет повторные поиски.
Как отобрать строки по реквизиту, которого нет в табличной части?
Только через запрос с соединением к нужной таблице (например, к справочнику номенклатуры) либо вычислением значения в цикле для каждой строки. НайтиСтроки() работает только по собственным реквизитам коллекции.
Что важно запомнить
Основной инструмент отбора в табличной части — НайтиСтроки() со структурой условий на равенство; для одной строки используйте Найти() с проверкой на Неопределено. Нестрогие условия реализуются перебором, выборка по базе — запросом, а отбор для пользователя на форме — свойством ОтборСтрок.