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

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

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

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

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

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

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

Что такое выборка из таблицы значений и когда она нужна

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

Типовые ситуации, где требуется выборка: поиск строки по номенклатуре перед добавлением новой (чтобы не было дублей), суммирование количества по конкретному складу, перенос отобранных строк в другую таблицу, проверка заполнения табличной части документа. Почти в каждой обработке загрузки данных из Excel или обмена между конфигурациями встречается конструкция вида «найти строку, если нет — добавить».

Метод Найти: поиск одной строки

Метод Найти(Значение, Колонки) возвращает первую строку, в которой указанные колонки равны искомому значению. Если строка не найдена, метод возвращает Неопределено — это важно проверять перед обращением к полям результата, иначе получите ошибку «Поле объекта не обнаружено».

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

ТаблицаТоваров = РезультатЗапроса.Выгрузить();

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

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

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

Иначе

Сообщить("Найден товар: " + НайденнаяСтрока.Наименование

+ ", остаток: " + НайденнаяСтрока.Остаток);

КонецЕсли;

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

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

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

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

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

Отбор.Вставить("Склад", СкладОтгрузки);

Отбор.Вставить("Статус", "КОтгрузке");

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

Если НайденныеСтроки.Количество() = 0 Тогда

Сообщить("Нет заказов к отгрузке по складу " + СкладОтгрузки);

Иначе

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

ОбщееКоличество = ОбщееКоличество + СтрокаЗаказа.Количество;

КонецЦикла;

КонецЕсли;

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

Порядок выборки через НайтиСтроки
  1. 1
    Создайте структуру: Новый Структура, добавьте пары «имя колонки — значение»
  2. 2
    Вызовите Таблица.НайтиСтроки(Структура) и сохраните результат в переменную
  3. 3
    Проверьте НайденныеСтроки.Количество() — массив может быть пустым
  4. 4
    Обойдите массив циклом Для Каждого и обработайте каждую строку
  5. 5
    Если строки нужно изменить — меняйте их прямо в цикле: это ссылки на строки исходной таблицы

Обход таблицы в цикле со сложными условиями

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

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

Если СтрокаТаблицы.Количество > 0

И СтрокаТаблицы.ДатаПоступления >= НачалоМесяца

И СтрНайти(СтрокаТаблицы.Наименование, "кабель") > 0 Тогда

// строка подходит под условие — обрабатываем

СуммаИтог = СуммаИтог + СтрокаТаблицы.Сумма;

КонецЕсли;

КонецЦикла;

Для обхода по индексу используют цикл Для ... По ... Цикл с обращением к строкам через квадратные скобки: Таблица[Индекс]. Такой вариант нужен, когда внутри цикла требуется знать позицию строки или удалять строки — при удалении в цикле «Для Каждого» коллекция изменяется прямо во время обхода, что приводит к пропуску элементов. Удалять строки безопаснее в обратном порядке — от последней к первой.

Для Индекс = 1 - ТаблицаДанных.Количество() По 0 Цикл

// корректнее: обратный обход

КонецЦикла;

// практичный вариант удаления:

ВсегоСтрок = ТаблицаДанных.Количество();

Для Сч = 1 По ВсегоСтрок Цикл

СтрокаТаблицы = ТаблицаДанных[ВсегоСтрок - Сч];

Если СтрокаТаблицы.Количество = 0 Тогда

ТаблицаДанных.Удалить(СтрокаТаблицы);

КонецЕсли;

КонецЦикла;

Интерактивный выбор строки и поиск по идентификатору

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

Для связи строки таблицы формы с данными используется НайтиПоИдентификатору(ИдентификаторСтроки). Идентификатор строки можно получить из элемента формы: Элементы.ТаблицаТоваров.ТекущаяСтрока. Это стандартный приём обработки двойного клика или выбора строки в динамических списках собственных таблиц.

&НаКлиенте

Процедура ТаблицаТоваровВыбор(Элемент, ВыбраннаяСтрока, Поле, СтандартнаяОбработка)

СтрокаДанных = ТаблицаТоваров.НайтиПоИдентификатору(ВыбраннаяСтрока);

Если СтрокаДанных <> Неопределено Тогда

ПоказатьПредупреждение(, "Выбран товар: " + СтрокаДанных.Наименование);

КонецЕсли;

КонецПроцедуры

Сравнение способов выборки

Найти()

Одна строка по равенству. Быстро, но возвращает только первое совпадение и требует проверки на Неопределено.

НайтиСтроки()

Все строки по отбору на равенство через структуру. Не поддерживает условия «больше», «содержит», «в списке».

Обход в цикле

Любые условия и логика. Медленнее на больших таблицах, требует аккуратности при удалении строк.

НайтиПоИдентификатору()

Только для реквизитов формы типа ТаблицаЗначений. Работает с идентификатором строки элемента формы.

Ускорение поиска: индексы и сортировка

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

Альтернативный приём — выгрузить нужные колонки в соответствие, где ключом будет искомое значение, а значением — строка или массив строк. Построение соответствия требует одного полного обхода, но все последующие поиски выполняются мгновенно. Это особенно эффективно в обработках сопоставления данных, где для каждой строки одной таблицы ищется пара в другой.

// Индекс по колонке поиска

ТаблицаТоваров.Индексы.Добавить("Номенклатура");

// Либо соответствие для многократного поиска

СоответствиеСтрок = Новый Соответствие;

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

СоответствиеСтрок.Вставить(СтрокаТ.Номенклатура, СтрокаТ);

КонецЦикла;

НайденнаяСтрока = СоответствиеСтрок.Получить(ИскомаяНоменклатура);

Если внутри цикла по одной таблице вы вызываете Найти() по другой таблице, общее время работы растёт как произведение размеров обеих таблиц. Замена внутреннего поиска на соответствие часто сокращает время обработки с минут до секунд — это первое, что стоит проверить при «тормозящей» обработке.
ПЗ
Практическое замечание

Типичные ошибки при выборке и как их избежать

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

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

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

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

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

Результат Найти()
Норма

Возвращена строка, поля доступны для чтения

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

Возвращено Неопределено — значение не найдено или не совпал тип

Массив НайтиСтроки()
Норма

Количество() больше нуля, строки соответствуют отбору

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

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

Изменение найденных строк
Норма

Изменения видны в исходной таблице — строки передаются по ссылке

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

Изменения потеряны — вероятно, работали с копией таблицы

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

Как найти строку по части значения, например по фрагменту наименования?

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

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

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

Можно ли отобрать строки по условию «ИЛИ» через НайтиСтроки?

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

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

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

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

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

Что быстрее для большой таблицы: НайтиСтроки или запрос?

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

Итог

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

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