К статье
Практический материал

Разработка и Конфигурация · 12 мин

Как отобрать строки в таблице значений 1С по условию

Как отобрать строки в таблице значений 1С по условию: методы НайтиСтроки, Найти, структура отбора, обход строк и выгрузка результата. Примеры кода и типичные ошибки.

Таблица значений — один из самых используемых объектов встроенного языка 1С: в неё выгружаются результаты запросов, в ней накапливаются данные перед записью в регистры или передачей в отчёты. Практически в каждой обработке возникает задача — получить не всю таблицу, а только строки, удовлетворяющие определённому условию: по контрагенту, по дате, по признаку проведения.

Платформа предлагает несколько способов такого отбора: методы НайтиСтроки() и Найти() со структурой отбора, обычный перебор строк в цикле с проверкой условия, а также предварительная фильтрация на уровне запроса. У каждого подхода есть свои ограничения: одни методы поддерживают только сравнение на равенство, другие требуют больше кода, но дают полную гибкость.

Ниже разобраны рабочие приёмы с примерами кода, особенности сравнения типов, способы удалить или скопировать найденные строки и ошибки, которые чаще всего приводят к «пустому» результату отбора.

Метод НайтиСтроки: базовый отбор по равенству

Основной инструмент отбора — метод НайтиСтроки(ПараметрыОтбора). В качестве параметра он принимает структуру, где ключ — имя колонки таблицы значений, а значение — искомое содержимое. Метод возвращает массив строк (объектов СтрокаТаблицыЗначений), которые удовлетворяют всем условиям одновременно, то есть условия объединяются по принципу «И».

Отбор = Новый Структура;

Отбор.Вставить("Контрагент", ВыбранныйКонтрагент);

Отбор.Вставить("Проведен", Истина);

НайденныеСтроки = ТаблицаДокументов.НайтиСтроки(Отбор);

Для Каждого Строка Из НайденныеСтроки Цикл

Сообщить("Накладная " + Строка.Номер + ", сумма: " + Строка.Сумма);

КонецЦикла;

Важно понимать: НайтиСтроки сравнивает значения только на равенство. Условия вида «Сумма > 10000» или «Дата в интервале» через структуру отбора передать нельзя — для них нужен перебор в цикле. Также сравнение выполняется с учётом типа: число 100 не равно строке "100", а пустая дата не равна Неопределено.

Если ни одна строка не подошла, метод возвращает пустой массив, а не Неопределено. Поэтому проверка результата выполняется через НайденныеСтроки.Количество() = 0, а не через сравнение с Неопределено — это частая ошибка начинающих разработчиков.

Метод Найти: когда нужна только одна строка

Если заранее известно, что под условие попадает максимум одна строка — например, поиск по уникальному коду или артикулу — удобнее метод Найти(Значение, ИмяКолонки). Он возвращает первую найденную строку или Неопределено, если совпадений нет.

СтрокаТовара = ТаблицаТоваров.Найти(АртикулПоставщика, "Артикул");

Если СтрокаТовара = Неопределено Тогда

Сообщить("Товар с артикулом " + АртикулПоставщика + " не найден");

Иначе

СтрокаТовара.Количество = СтрокаТовара.Количество + 1;

КонецЕсли;

У метода есть особенность: параметр с именем колонки можно опустить, тогда поиск пойдёт по всем колонкам таблицы. Такой вариант почти никогда не нужен на практике — он медленнее и может найти совпадение не в том поле. Всегда указывайте имя колонки явно.

Помните, что Найти останавливается на первом совпадении. Если в таблице несколько строк с одинаковым значением, остальные будут проигнорированы. Когда важна полнота результата, используйте НайтиСтроки.

Отбор по сложным условиям через перебор строк

Структура отбора поддерживает только равенство, поэтому условия сравнения, проверки на вхождение в список, поиск по подстроке и комбинации «ИЛИ» реализуются обычным циклом Для Каждого с оператором Если. Это самый гибкий способ, хотя и более многословный.

Для Каждого Строка Из ТаблицаПродаж Цикл

Если Строка.Сумма > 50000

И Строка.Дата >= НачалоМесяца(ТекущаяДатаСеанса()) Тогда

// строка подходит под условие

ОбработатьКрупнуюПродажу(Строка);

КонецЕсли;

КонецЦикла;

Для проверки вхождения значения в список допустимых вариантов удобно использовать массив или соответствие. Поиск по соответствию работает заметно быстрее, чем метод Найти массива, если таблица большая:

ДопустимыеСклады = Новый Соответствие;

ДопустимыеСклады.Вставить(СкладОсновной, Истина);

ДопустимыеСклады.Вставить(СкладРезервный, Истина);

Для Каждого Строка Из ТаблицаОстатков Цикл

Если ДопустимыеСклады[Строка.Склад] <> Неопределено Тогда

// склад из допустимого списка

КонецЕсли;

КонецЦикла;

Для поиска по фрагменту текста применяйте функцию СтрНайти() — она возвращает позицию вхождения подстроки или 0, если совпадений нет. Учтите, что поиск по подстроке регистрозависим: при необходимости приводите обе строки к одному регистру через ВРег() или НРег().

bsl
Для Каждого Строка Из ТаблицаТоваров Цикл
    Если СтрНайти(НРег(Строка.Наименование), НРег("кабель")) > 0 Тогда
        // наименование содержит слово "кабель" в любом регистре
    КонецЕсли;
КонецЦикла;

Что делать с отобранными строками: копирование, удаление, изменение

Найти строки — обычно только половина задачи. Дальше с ними нужно что-то сделать: перенести в другую таблицу, удалить из текущей или массово изменить значения колонок.

Для копирования в новую таблицу проще всего использовать метод Выгрузить() исходной таблицы с параметром отбора — он вернёт новую таблицу значений с теми же колонками, но только с подходящими строками:

Отбор = Новый Структура("Организация", ТекущаяОрганизация);

ТаблицаОтбор = ТаблицаДокументов.Выгрузить(Отбор);

Если копировать нужно в существующую таблицу с другой структурой колонок, добавляйте строки вручную в цикле и заполняйте нужные поля. Для удаления найденных строк из исходной таблицы используйте метод Удалить(), передавая в него найденную строку:

НайденныеСтроки = ТаблицаОстатков.НайтиСтроки(Новый Структура("Количество", 0));

Для Каждого Строка Из НайденныеСтроки Цикл

ТаблицаОстатков.Удалить(Строка);

КонецЦикла;

Порядок действий при отборе
  1. 1
    Определите тип условия: равенство — подойдёт НайтиСтроки, сравнение или ИЛИ — нужен цикл
  2. 2
    Сформируйте структуру отбора с точными именами колонок таблицы
  3. 3
    Вызовите НайтиСтроки и проверьте Количество() результата
  4. 4
    Обработайте найденные строки: измените, скопируйте через Выгрузить или удалите
  5. 5
    Проверьте результат на контрольном примере с известным числом строк

Почему отбор не находит строки: типичные причины

Самая частая жалоба — «НайтиСтроки возвращает пустой массив, хотя данные в таблице точно есть». В подавляющем большинстве случаев причина в несовпадении типов или значений, которое не видно глазом.

Проверьте типы сравниваемых значений. Ссылка на элемент справочника не равна строке с его наименованием, число не равно его строковому представлению, а NULL, попавший в таблицу из запроса с левым соединением, не равен ни пустой ссылке, ни Неопределено. Перед отбором полезно вывести тип значения в отладке: ТипЗнч(Строка.Контрагент).

Вторая причина — невидимые символы в строках: пробелы в конце, неразрывные пробелы, разный регистр букв. Сравнение строк в НайтиСтроки регистрозависимое и посимвольное. Если данные пришли из внешнего файла или обмена, нормализуйте их функцией СокрЛП() при загрузке.

Третья причина — опечатка в имени колонки в структуре отбора. Если колонки с таким именем нет, платформа выдаст ошибку, но если имя случайно совпало с другой колонкой — отбор молча сработает неправильно. Проверяйте состав колонок через Таблица.Колонки.

Типичные ошибки
01
Проверка результата НайтиСтроки через сравнение с Неопределено — метод возвращает пустой массив, поэтому проверять нужно Количество()
02
Сравнение ссылки со строкой или числа со строкой — отбор всегда пуст из-за разных типов
03
Удаление строк внутри цикла обхода той же таблицы — часть строк пропускается
04
Ожидание от Найти() всех совпадений — метод возвращает только первую строку
05
Поиск по подстроке через структуру отбора — поддерживается только точное равенство

Производительность и альтернатива: отбор на уровне запроса

На небольших таблицах (сотни и тысячи строк) разница между способами отбора незаметна. Но если таблица содержит десятки тысяч строк и отбор выполняется многократно в цикле, стоит задуматься об оптимизации.

Главный приём — не загружать лишние данные вообще. Если таблица значений формируется запросом к базе данных, перенесите условия в секцию ГДЕ запроса: СУБД отфильтрует записи быстрее и эффективнее, чем перебор в памяти, а в таблицу попадут только нужные строки.

Если по одной и той же таблице многократно ищутся строки по значению колонки, полезно добавить индекс: Таблица.Индексы.Добавить("Артикул"). После этого методы Найти и НайтиСтроки по проиндексированной колонке выполняются значительно быстрее. Индексы имеет смысл создавать только при большом объёме данных и повторных поисках — на разовый отбор затраты на построение индекса не окупятся.

Сравнение вариантов отбора

НайтиСтроки со структурой

Подходит для отбора по равенству одного или нескольких полей. Ограничение: нельзя задать сравнение, ИЛИ или поиск по подстроке

Найти

Подходит, когда нужна одна строка по уникальному значению. Ограничение: возвращает только первое совпадение

Перебор в цикле с Если

Подходит для любых условий: сравнения, списки, подстроки, ИЛИ. Ограничение: больше кода, медленнее на больших таблицах

Отбор в запросе (ГДЕ)

Оптимален, если данные ещё не загружены из базы. Ограничение: неприменим к уже существующей таблице в памяти

Контрольные точки

Как проверить результат

Количество найденных строк
Норма

НайденныеСтроки.Количество() больше нуля и совпадает с ожидаемым по контрольным данным

Тревожный признак

Пустой массив при заведомо существующих данных — проверяйте типы и значения отбора

Содержимое найденных строк
Норма

Значения полей соответствуют условию отбора

Тревожный признак

В выборке строки, не подходящие под условие, — вероятно, перепутано имя колонки

Исходная таблица после удаления строк
Норма

Удалены только целевые строки, остальные на месте

Тревожный признак

Пропущены часть строк или удалено лишнее — значит, удаление выполнялось в цикле обхода

Ещё одна альтернатива для управляемых форм — динамический список с установленным отбором, если данные отображаются пользователю, и метод Скопировать() таблицы значений с параметром отбора, когда нужна копия таблицы с фильтром в одну строку кода: НоваяТаблица = Таблица.Скопировать(Отбор).

Часто задаваемые вопросы

Можно ли в НайтиСтроки задать условие «больше» или «меньше»?

Нет, структура отбора поддерживает только сравнение на равенство. Для условий «больше», «меньше», «между» перебирайте строки циклом Для Каждого с оператором Если, либо формируйте таблицу запросом с условиями в секции ГДЕ.

Чем отличается НайтиСтроки от Найти?

НайтиСтроки возвращает массив всех строк, подходящих под отбор, а Найти — только первую найденную строку или Неопределено. Найти принимает значение и имя колонки напрямую, НайтиСтроки — структуру с произвольным числом условий.

Почему НайтиСтроки не находит строку, хотя значение в таблице есть?

Чаще всего не совпадают типы (ссылка и строка, число и строка) или в строковом значении есть лишние пробелы и отличается регистр. Также значение могло прийти из запроса как NULL. Проверьте тип через ТипЗнч() и нормализуйте строки функцией СокрЛП().

Как отобрать строки по нескольким значениям одной колонки (условие ИЛИ)?

Структура отбора объединяет условия только по «И». Для «ИЛИ» используйте цикл с проверкой вхождения значения в массив или соответствие, либо несколько вызовов НайтиСтроки с объединением результатов, либо перенесите условие в запрос с оператором В.

Как удалить из таблицы все строки, подходящие под условие?

Получите их через НайтиСтроки(Отбор) и в отдельном цикле вызовите Таблица.Удалить(Строка) для каждой. Нельзя удалять строки прямо внутри обхода той же таблицы в цикле Для Каждого — обход собьётся и часть строк останется.

Ускорит ли поиск добавление индекса таблице значений?

Да, метод Индексы.Добавить("ИмяКолонки") ускоряет Найти и НайтиСтроки по этой колонке. Индекс оправдан при больших таблицах и многократных поисках; для разового отбора по небольшой таблице эффекта не будет.

Итог

Что важно запомнить

Для отбора по равенству используйте НайтиСтроки со структурой отбора и проверяйте результат через Количество(). Для одной строки — Найти с явным именем колонки. Сложные условия реализуйте перебором в цикле, а данные из базы лучше фильтровать сразу в запросе. Не удаляйте строки внутри цикла обхода и следите за соответствием типов сравниваемых значений.