Отбор строк из таблицы значений, табличной части и справочника в коде 1С 8.3

· 9 мин

Содержание

Чтобы выбрать строки в 1С по точному значению, вызовите у коллекции НайтиСтроки(Новый Структура("Колонка", Значение)) — метод вернёт массив подходящих строк. Одну первую строку даёт Найти(Значение, "Колонка"), а сложные условия («больше», «содержит», «входит в список») решает запрос или перебор в цикле.

Методы выбора строк почти одинаково работают в таблицах значений, табличных частях документов и данных формы, но различия есть, и именно на них код падает. Тема общая для платформы 8.3 и не зависит от конфигурации: БП, ЗУП и УТ используют одни и те же методы встроенного языка.

Найти и НайтиСтроки: базовые способы выбрать строку в коллекции

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

МетодЧто возвращаетУсловиеКогда брать
Найти(Значение, Колонки)первую найденную строку или Неопределеноодно значение, ищется в перечисленных колонкахпроверить наличие, взять одну строку
НайтиСтроки(Структура)массив строк (может быть пустым)равенство по нескольким колонкам сразувыбрать все строки по условию «И»
Скопировать(Отбор, Колонки)новая таблица значенийструктура отбора или массив строкнужна отдельная таблица для дальнейшей обработки

Метод Найти

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

Строка = ТаблицаТоваров.Найти(Номенклатура, "Номенклатура");
Если Строка = Неопределено Тогда
	Строка = ТаблицаТоваров.Добавить();
	Строка.Номенклатура = Номенклатура;
КонецЕсли;
Строка.Количество = Строка.Количество + 1;

Это классический приём «найти или добавить» — так сворачивают повторы при загрузке данных.

Метод НайтиСтроки

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

Отбор = Новый Структура;
Отбор.Вставить("Склад", ОсновнойСклад);
Отбор.Вставить("Обработана", Ложь);

МассивСтрок = ТаблицаТоваров.НайтиСтроки(Отбор);
Для Каждого Строка Из МассивСтрок Цикл
	Строка.Обработана = Истина;
КонецЦикла;

Массив содержит ссылки на те же строки исходной таблицы, а не копии. Меняете поле в найденной строке — меняется исходная таблица.

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

Метод Скопировать с отбором

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

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

Связка Скопировать + Свернуть даёт итоги по номенклатуре одного склада в две строки кода и не портит исходную таблицу.

Другие коллекции

  • 📋 Массив.Найти(Значение) — возвращает индекс элемента или Неопределено.
  • 🗂 СписокЗначений.НайтиПоЗначению(Значение) — возвращает элемент списка.
  • 🔑 Соответствие.Получить(Ключ) — самый быстрый поиск по ключу; если ключа нет, вернётся Неопределено.

Как выбрать строки в табличной части документа

Табличная часть объекта (ДокументОбъект.Товары) поддерживает те же Найти и НайтиСтроки. Код из предыдущего раздела переносится без изменений, если вы работаете с объектом в модуле объекта или на сервере.

ДокументОбъект = Ссылка.ПолучитьОбъект();
Отбор = Новый Структура("Номенклатура", Номенклатура);
Строки = ДокументОбъект.Товары.НайтиСтроки(Отбор);

На форме картина другая. Реквизит формы с табличной частью — это ДанныеФормыКоллекция. У неё есть НайтиСтроки(Структура) и НайтиПоИдентификатору(Идентификатор), но нет метода Найти. Поэтому код, скопированный из модуля объекта в модуль формы, иногда падает на вызове Найти — заменяйте его на НайтиСтроки с одним ключом.

Выделенные строки в таблице формы

Когда пользователь выделил несколько строк мышью, взять их можно через свойство элемента формы:

&НаКлиенте
Процедура ОбработатьВыделенные(Команда)
	Для Каждого Идентификатор Из Элементы.Товары.ВыделенныеСтроки Цикл
		ДанныеСтроки = Объект.Товары.НайтиПоИдентификатору(Идентификатор);
		Если ДанныеСтроки <> Неопределено Тогда
			ДанныеСтроки.Скидка = 5;
		КонецЕсли;
	КонецЦикла;
КонецПроцедуры

ВыделенныеСтроки хранит идентификаторы, а не сами строки. Отсюда обязательная связка с НайтиПоИдентификатору. Текущую (одну активную) строку дают Элементы.Товары.ТекущиеДанные и Элементы.Товары.ТекущаяСтрока.

Показать на экране только нужные строки

Если строки надо не обработать, а скрыть лишние от пользователя, данные не трогайте. Задайте отбор отображения:

Элементы.Товары.ОтборСтрок = Новый ФиксированнаяСтруктура("Склад", ОсновнойСклад);

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

Выбор строк запросом: условия, которые не умеет НайтиСтроки

НайтиСтроки проверяет только равенство. Как только нужно «сумма больше 10 000», «дата в периоде», «наименование содержит слово», «группировка с суммой» — переходите на запрос.

Запрос к табличной части в базе

Если документ записан, читать его табличную часть объектом не нужно. Запрос заберёт только нужные строки и только нужные колонки:

Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
|	Т.НомерСтроки,
|	Т.Номенклатура,
|	Т.Количество,
|	Т.Сумма
|ИЗ
|	Документ.РеализацияТоваровУслуг.Товары КАК Т
|ГДЕ
|	Т.Ссылка = &Ссылка
|	И Т.Сумма > &МинСумма";
Запрос.УстановитьПараметр("Ссылка", Ссылка);
Запрос.УстановитьПараметр("МинСумма", 10000);
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
	// Выборка.Номенклатура, Выборка.Сумма
КонецЦикла;

Имя документа подставьте своё: в БП 3.0 и УТ 11 реализация называется РеализацияТоваровУслуг, но в доработанной конфигурации всё может быть иначе.

Запрос к таблице значений из памяти

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

Запрос = Новый Запрос;
Запрос.МенеджерВременныхТаблиц = Новый МенеджерВременныхТаблиц;
Запрос.Текст =
"ВЫБРАТЬ
|	Т.Номенклатура,
|	Т.Количество,
|	Т.Сумма
|ПОМЕСТИТЬ ВТ_Товары
|ИЗ
|	&ТаблицаТоваров КАК Т
|;
|ВЫБРАТЬ
|	ВТ.Номенклатура,
|	СУММА(ВТ.Сумма) КАК Сумма
|ИЗ
|	ВТ_Товары КАК ВТ
|ГДЕ
|	ВТ.Количество > 0
|СГРУППИРОВАТЬ ПО
|	ВТ.Номенклатура";
Запрос.УстановитьПараметр("ТаблицаТоваров", ТаблицаТоваров);
Результат = Запрос.Выполнить().Выгрузить();

У колонок передаваемой таблицы должны быть заданы типы. Колонку, созданную через Колонки.Добавить("Сумма") без описания типов, запрос не поймёт. Создавайте так: Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число")).

Запрос к таблице значений выполняется на сервере. С клиента так не сделать: вызывайте серверную процедуру и передавайте ей данные.

Полезные конструкции для отбора

  • 🔢 ВЫБРАТЬ ПЕРВЫЕ 10 — ограничить число строк.
  • 📚 ГДЕ Т.Склад В (&СписокСкладов) — параметр-массив или список значений.
  • 🔤 ГДЕ Т.Номенклатура.Наименование ПОДОБНО "%кабель%" — поиск по части строки.
  • 📅 ГДЕ Т.Период МЕЖДУ &Начало И &Конец — диапазон дат.

Перебор строк с условием: когда цикл честнее запроса

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

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

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

Удаление выбранных строк

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

Первый — собрать строки и удалить их потом:

КУдалению = ТаблицаТоваров.НайтиСтроки(Новый Структура("Количество", 0));
Для Каждого Строка Из КУдалению Цикл
	ТаблицаТоваров.Удалить(Строка);
КонецЦикла;

Второй — обратный проход по индексу, с границей, вычисленной один раз до цикла:

Всего = ТаблицаТоваров.Количество();
Для Сч = 1 По Всего Цикл
	Индекс = Всего - Сч;
	Если ТаблицаТоваров[Индекс].Количество = 0 Тогда
		ТаблицаТоваров.Удалить(Индекс);
	КонецЕсли;
КонецЦикла;

При Всего = 3 индекс идёт 2, 1, 0. Удаление строки с большим индексом не сдвигает строки, до которых цикл ещё не дошёл.

Выбор элементов справочника

Для справочников у менеджера есть готовые методы поиска:

МетодПримерОсобенность
НайтиПоКодуСправочники.Номенклатура.НайтиПоКоду("00-00000123")код передаётся строкой с ведущими нулями
НайтиПоНаименованиюСправочники.Склады.НайтиПоНаименованию("Основной склад", Истина)Истина — точное совпадение
НайтиПоРеквизитуСправочники.Контрагенты.НайтиПоРеквизиту("ИНН", ИНН)вернёт первый найденный элемент

Если элемент не найден, методы возвращают пустую ссылку, а не Неопределено. Проверка — Если Результат.Пустая() Тогда. Когда совпадений может быть несколько (два контрагента с одним ИНН), берите запрос: НайтиПоРеквизиту молча отдаст только один.

Скорость: какой метод выбора строк быстрее

На таблице из 200 строк разницы нет — пишите как удобнее. Проблемы начинаются, когда поиск вызывается внутри цикла по другой большой таблице. Тысяча вызовов Найти по таблице в 50 000 строк без индекса — это до 50 миллионов сравнений.

Индексы таблицы значений

Индекс строится один раз, после этого НайтиСтроки по проиндексированным колонкам не перебирает таблицу целиком:

ТаблицаОстатков.Индексы.Добавить("Номенклатура, Склад");
// далее много вызовов
Строки = ТаблицаОстатков.НайтиСтроки(Новый Структура("Номенклатура, Склад", Товар, Склад));

Набор колонок в отборе должен совпадать с набором в индексе. Индекс по «Номенклатура, Склад» не ускорит отбор только по «Склад».

Сравнение подходов

ПодходСкорость на больших объёмахГде работаетОграничения
Найти без индексамедленно, переборклиент и сервертолько одно значение
НайтиСтроки с индексомбыстросервер, таблица значенийтолько равенство
Соответствие по ключубыстроклиент и серверключ — одно значение, нужна сборка
Запросбыстро, особенно к данным в базетолько сервертипизированные колонки, накладные расходы на вызов
Перебор в циклемедленно, линейноклиент и серверзато любые условия

Практические приёмы

  1. Вынесите поиск из вложенного цикла. Постройте индекс или Соответствие один раз, потом ищите.
  2. Не читайте документ объектом ради одной колонки. ПолучитьОбъект() загружает все табличные части; запрос берёт только нужное.
  3. Не гоняйте таблицы между клиентом и сервером ради отбора. Отберите на сервере и верните на форму результат.
  4. Если нужен только список значений колонки — ТаблицаТоваров.ВыгрузитьКолонку("Номенклатура") вернёт массив без цикла.
Какой способ выбора строк подходит вашей задаче?

Ошибки при выборе строк и как их избежать

«Значение не является значением объектного типа»

Классика после Найти: строка не нашлась, вернулось Неопределено, а код сразу обращается к Строка.Количество. Лечится проверкой результата перед использованием.

«Поле объекта не обнаружено»

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

Пустой результат, хотя строки видны

  • 🔁 Сравниваются значения разных типов: в колонке ссылка, а ищете строку с наименованием. Найти сравнивает значения, а не их представление.
  • 🔢 Число хранится в колонке как строка (после загрузки из Excel). 100 и "100" не равны.
  • ✂️ В строке хвостовые пробелы. Перед поиском применяйте СокрЛП.
  • 🗓 Дата со временем: НайтиСтроки по дате 01.03 не найдёт строку с 01.03 14:25. Отбирайте по НачалоДня или запросом с МЕЖДУ.

Изменения «не сохраняются»

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

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

Частые вопросы

Можно ли в НайтиСтроки задать условие «ИЛИ»?
Нет, условия структуры соединяются только через «И». Для «ИЛИ» вызовите метод дважды и объедините массивы либо используйте запрос.
Учитывает ли поиск регистр букв?
Сравнение строк в Найти и НайтиСтроки точное. Для поиска без учёта регистра перебирайте строки и сравнивайте через НРег или используйте ПОДОБНО в запросе.
Как выбрать строки из дерева значений?
У коллекции Строки дерева значений тоже есть Найти и НайтиСтроки. Отдельный параметр включает поиск во вложенных уровнях; без него метод смотрит только строки текущего уровня.
Чем ВыгрузитьКолонку отличается от НайтиСтроки?
ВыгрузитьКолонку возвращает массив значений одной колонки по всем строкам, без отбора. НайтиСтроки возвращает сами строки, подходящие под условие.
Почему индекс не ускорил поиск?
Чаще всего набор колонок в отборе не совпадает с набором в индексе или поиск идёт методом без использования индекса. Проверьте, что ключи структуры и колонки индекса одинаковые.