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