Особенности поиска по полю НомерСтроки в табличных частях и таблицах значений

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В процессе разработки на платформе «1С:Предприятие 8» программисты часто сталкиваются с необходимостью быстрого поиска данных в таблицах. Казалось бы, методы Найти() и НайтиСтроки() интуитивно понятны, однако при использовании системного поля НомерСтроки поведение системы может стать непредсказуемым для неофита. В этой статье мы подробно разберем, почему один и тот же код выдает разные результаты в зависимости от контекста и типа объекта, а также рассмотрим критические изменения в поведении платформы начиная с версии 8.3.24.

Постановка задачи и исходные данные

Представим ситуацию: у нас есть документ «Заказ клиента» с табличной частью Товары. В этой таблице содержатся четыре строки:

  1. Товар1 (Номер строки 1)
  2. Товар2 (Номер строки 2)
  3. Товар3 (Номер строки 3)
  4. Товар1 (Номер строки 4) — обратите внимание, номенклатура дублируется.

Рассмотрим код, который выполняет различные варианты поиска, и проанализируем результаты каждого шага.


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

// Вариант 1: Поиск методом Найти
МояСтрока = об.Товары.Найти(1, "НомерСтроки");

// Вариант 2: Поиск методом НайтиСтроки в ТЧ по номеру
Мас1 = об.Товары.НайтиСтроки(New Структура("НомерСтроки", 1));

// Вариант 3: Поиск методом НайтиСтроки по номеру и номенклатуре
Мас3 = об.Товары.НайтиСтроки(New Структура("НомерСтроки, Номенклатура", 1, об.Товары[0].Номенклатура));

// Вариант 4: Поиск в выгруженной Таблице Значений
Мас4 = об.Товары.Выгрузить().НайтиСтроки(New Структура("НомерСтроки, Номенклатура", 1, об.Товары[0].Номенклатура));

Разбор варианта №1: Метод Найти в табличной части

Начнем с классического метода Найти(). При выполнении кода об.Товары.Найти(1, "НомерСтроки") переменная МояСтрока будет содержать первую строку табличной части. Несмотря на то, что НомерСтроки является системным полем, метод Найти() умеет работать с ним напрямую.

Важный нюанс: До версии платформы 8.3.24 у этого метода существовала особенность (баг, ставший фичей). Если вы указывали колонку поиска НомерСтроки, но искомое значение не находилось (например, вы искали строку номер 10, а их всего 5), 1С не возвращала Неопределено сразу. Вместо этого платформа начинала «жадный» поиск по всем остальным колонкам. Если в какой-то строке в колонке «Количество» или «Сумма» встречалось число 10, метод возвращал эту строку. В современных версиях (8.3.24+) это поведение исправлено: поиск ограничен строго указанной колонкой.

Разбор варианта №2: Почему НайтиСтроки возвращает пустой массив в ТЧ

Рассмотрим вызов об.Товары.НайтиСтроки(New Структура("НомерСтроки", 1)). Здесь многих ждет сюрприз: Мас1.Количество() будет равно 0.

Почему так происходит? Дело в том, что объект ТабличнаяЧасть воспринимает НомерСтроки как вычисляемое системное свойство, а не как полноценный реквизит данных. Метод НайтиСтроки() спроектирован для работы с физическими колонками данных. Поскольку в метаданных табличной части НомерСтроки не является хранимым реквизитом (он генерируется динамически), метод просто «не видит» эту колонку в структуре поиска для объекта ТЧ.

Разбор варианта №3: Неявное игнорирование параметров поиска

Теперь проанализируем ситуацию с поиском по двум полям: об.Товары.НайтиСтроки(New Структура("НомерСтроки, Номенклатура", 1, "Товар1")). Логично было бы ожидать, что мы получим одну строку (первую), так как только она соответствует обоим условиям.

Однако результат Мас3.Количество() будет равен 2. Проанализируем причину этого явления:

  1. Платформа получает структуру поиска с двумя ключами: НомерСтроки и Номенклатура.
  2. При работе с объектом ТабличнаяЧасть, 1С отбрасывает ключ НомерСтроки, так как считает его недопустимым для этого метода.
  3. В итоге поиск выполняется только по номенклатуре. Поскольку «Товар1» встречается в первой и четвертой строках, метод возвращает обе эти строки.

Это крайне опасная ситуация, так как она не вызывает исключения (ошибки), но возвращает неверные с точки зрения бизнес-логики данные. Программист может ожидать уникальный результат, а получить массив «дублей».

Разбор варианта №4: Превращение в Таблицу Значений

Последний пример: об.Товары.Выгрузить().НайтиСтроки(...). Здесь Мас4.Количество() будет равно 1. Это правильный результат.

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

Сравнительный анализ и рекомендации

Проанализировав все варианты, мы можем сделать важные выводы для практической работы:

  1. Тип объекта имеет значение. Методы поиска ведут себя по-разному для ТабличнойЧасти (объекта документа/справочника) и для ТаблицыЗначений (универсальной коллекции в памяти).
  2. Избегайте поиска по НомерСтроки в ТЧ через структуры. Если вам нужно найти конкретную строку в объекте по её номеру, используйте метод Найти() или обращайтесь напрямую по индексу: об.Товары[НужныйНомер - 1].
  3. Выгрузка в ТЗ для сложных выборок. Если требуется искать строки по сложному набору условий, включая номер строки, лучше предварительно выгрузить табличную часть в ТаблицуЗначений. Это сделает поиск предсказуемым.
  4. Производительность. Помните, что поиск по неиндексированным колонкам в большой таблице (как в ТЧ, так и в ТЗ) — это линейный перебор. Если таблица содержит тысячи строк и поиск выполняется часто, добавление индекса в ТаблицуЗначений (ТЗ.Индексы.Добавить("Номенклатура")) значительно ускорит работу НайтиСтроки(). В объекте ТабличнаяЧасть создать индекс программно невозможно.

Идентификатор строки как альтернатива

В контексте управляемых форм для работы с конкретными строками рекомендуется использовать ИдентификаторСтроки, а не её номер. Номер строки может измениться при сортировке или удалении других строк, тогда как идентификатор остается неизменным в течение сеанса работы с формой. Это позволяет избежать ошибок, когда код обращается к «строке №5», которая после удаления «строки №2» стала «строкой №4».

Рассмотрим пример получения строки по идентификатору в контексте формы:


// Получаем идентификатор текущей строки
Идентификатор = Элементы.Товары.ТекущаяСтрока;

// Находим данные этой строки в коллекции
СтрокаДанных = Объект.Товары.НайтиПоИдентификатору(Идентификатор);

Этот метод является наиболее надежным и «православным» с точки зрения архитектуры 1С:Предприятия при работе в интерфейсе. При необходимости реализовать фильтрацию подчиненных данных по текущей строке можно использовать механизм связывания табличных частей на управляемых формах.

Заключение

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

← На главную