Отбор строк в таблице значений 1С: от НайтиСтроки до запроса
Содержание
Быстрее всего отфильтровать таблицу значений в 1С методом НайтиСтроки: передаёте ему структуру «колонка — значение» и получаете массив подходящих строк. Исходная таблица не меняется. Если нужна не выборка строк, а новая таблица, оберните результат в Скопировать.
Но НайтиСтроки умеет только равенство. «Больше», «между», «начинается с», «не пусто» — это уже цикл или запрос к таблице. Ниже все способы по порядку, от простого к сложному, с кодом и ограничениями каждого.
У самой таблицы значений метода Выбрать нет. Он есть у результата запроса и у менеджеров записей. Если видите «отбор через Выбрать» в таблице значений, речь о запросе, который читает эту таблицу как источник (см. раздел про запрос).
Какой способ выбрать
Прежде чем писать код, определитесь, что именно вам нужно на выходе.
| Способ | Что даёт | Условия | Когда брать |
|---|---|---|---|
Найти | одна строка | равенство по одному значению | нужна первая попавшаяся строка |
НайтиСтроки | массив строк | только равенство, несколько колонок через И | типовой отбор |
Скопировать | новая таблица | строки из массива или все | нужна отдельная таблица результата |
Цикл с Если | что угодно | любые | диапазоны, маски, сложные условия |
| Запрос к таблице | новая таблица | любые, плюс группировка | большие объёмы и сложная логика |
Отбор строк методом НайтиСтроки
Метод принимает структуру. Ключ структуры — имя колонки, значение — то, что должно в ней стоять. Несколько ключей работают как условие «И».
Отбор = Новый Структура;
Отбор.Вставить("Организация", Организация);
Отбор.Вставить("Проведен", Истина);
НайденныеСтроки = Таблица.НайтиСтроки(Отбор);
Для Каждого Строка Из НайденныеСтроки Цикл
// работаем со строкой исходной таблицы
Строка.Сумма = Строка.Сумма * 2;
КонецЦикла;
Что важно знать:
- ✅ В массиве лежат те же строки, что и в таблице, а не копии. Изменили значение в строке — изменилась и таблица.
- ✅ Пустой отбор (пустая структура) вернёт все строки.
- ✅ Если ничего не нашлось, придёт пустой массив, не
Неопределено. ПроверяйтеНайденныеСтроки.Количество() = 0. - ⚠️ Имя ключа структуры обязано совпадать с именем колонки. Опечатка даст ошибку времени выполнения про отсутствующую колонку.
- ⚠️ Ключом структуры не может быть имя с пробелом или точкой. Если у колонки такое имя, переименуйте её при создании таблицы.
Когда нужна одна строка
Для поиска одного значения по одной колонке проще метод Найти:
Строка = Таблица.Найти("0000123", "Код");
Если Строка <> Неопределено Тогда
Сообщить(Строка.Наименование);
КонецЕсли;
Второй параметр можно не указывать: тогда поиск пойдёт по всем колонкам. Это медленнее и даёт случайные совпадения, так что колонку лучше называть всегда.
Условие «или» по одной колонке
Структура отбора склеивает условия только через «И». Чтобы получить строки, где склад равен одному из нескольких значений, соберите результат из нескольких вызовов:
Подходящие = Новый Массив;
Для Каждого Склад Из МассивСкладов Цикл
Для Каждого Строка Из Таблица.НайтиСтроки(Новый Структура("Склад", Склад)) Цикл
Подходящие.Добавить(Строка);
КонецЦикла;
КонецЦикла;
Дублей здесь не будет, пока значения в массиве складов не повторяются. Если повторяются, сначала уберите повторы. Для длинных списков (сотни значений) лучше запрос с условием В (&Список): он справится быстрее и короче.
Как получить отфильтрованную копию таблицы
Массив строк не всегда удобен: его нельзя отправить в табличный документ или передать в запрос как таблицу. Чтобы получить именно таблицу, скопируйте найденные строки:
НайденныеСтроки = Таблица.НайтиСтроки(Новый Структура("Склад", Склад));
Результат = Таблица.Скопировать(НайденныеСтроки);
Вторым параметром Скопировать принимает список колонок через запятую. Так можно сразу отрезать лишнее:
Результат = Таблица.Скопировать(НайденныеСтроки, "Номенклатура, Количество");
Копия независима: правки в Результат исходную таблицу не затронут. Для больших таблиц помните, что копирование занимает память, и держите в голове цену этой операции.
Есть и обратная задача: оставить в таблице только нужное. Тогда найдите ненужные строки и удалите их. Удалять надо не во время обхода той же таблицы, а по заранее собранному массиву:
ЛишниеСтроки = Таблица.НайтиСтроки(Новый Структура("Удалить", Истина));
Для Каждого Строка Из ЛишниеСтроки Цикл
Таблица.Удалить(Строка);
КонецЦикла;
Массив собран до удаления, поэтому обход безопасен.
Фильтр в цикле, когда равенства мало
НайтиСтроки не умеет диапазоны, сравнения и маски. Для них пишут цикл и собирают подходящие строки в массив:
Подходящие = Новый Массив;
Для Каждого Строка Из Таблица Цикл
Если Строка.Сумма >= 10000
И Строка.Сумма <= 50000
И СтрНачинаетсяС(Строка.Наименование, "Кабель") Тогда
Подходящие.Добавить(Строка);
КонецЕсли;
КонецЦикла;
Результат = Таблица.Скопировать(Подходящие);
Здесь отбор: сумма от 10 000 до 50 000 включительно и наименование начинается со слова «Кабель». Равенством такое не выразить.
Регистр и пробелы в строках
Сравнение строк в 1С чувствительно к регистру. «кабель» и «Кабель» для НайтиСтроки разные значения. Если данные приходят из внешнего файла, приводите их заранее:
Если ВРег(СокрЛП(Строка.Наименование)) = ВРег(СокрЛП(Искомое)) Тогда
// совпало без учёта регистра и лишних пробелов
КонецЕсли;
В НайтиСтроки так сделать нельзя, поэтому для «грязных» данных цикл надёжнее. Либо один раз добавьте в таблицу колонку с нормализованным значением и ищите по ней.
Как удалять строки прямо в цикле
Нельзя обходить таблицу «Для Каждого» и удалять из неё же: порядок съедет и часть строк пропустится. Два рабочих варианта:
- Собрать массив на удаление и удалить после обхода (пример выше).
- Идти с конца индексами.
Для Индекс = 1 По Таблица.Количество() Цикл
Строка = Таблица[Таблица.Количество() - Индекс];
Если Строка.Количество = 0 Тогда
Таблица.Удалить(Строка);
КонецЕсли;
КонецЦикла;
Количество пересчитывается на каждом шаге, а идём мы от последней строки к первой, так что сдвиг индексов нам не мешает.
Запрос к таблице значений для сложного отбора
Когда условий много, нужны группировки, соединение с другой таблицей или вычисляемые поля, проще передать таблицу в запрос параметром и отобрать на языке запросов.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Т.Номенклатура КАК Номенклатура,
| СУММА(Т.Количество) КАК Количество
|ИЗ
| &Таблица КАК Т
|ГДЕ
| Т.Сумма > &МинСумма
| И Т.Склад В (&Склады)
|СГРУППИРОВАТЬ ПО
| Т.Номенклатура";
Запрос.УстановитьПараметр("Таблица", Таблица);
Запрос.УстановитьПараметр("МинСумма", 1000);
Запрос.УстановитьПараметр("Склады", МассивСкладов);
Результат = Запрос.Выполнить().Выгрузить();
Вот тут и появляется Выбрать: если вам нужен обход без выгрузки, вызовите Запрос.Выполнить().Выбрать() и читайте записи через Пока Выборка.Следующий() Цикл.
Что учесть:
- ⚠️ Колонки таблицы-параметра должны иметь заданные типы. Если создали таблицу без типов (
Таблица.Колонки.Добавить("Сумма")), запрос может отказаться работать с ней или вести себя непредсказуемо. Типы указывайте сразу:Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число")). - ✅ Запрос удобен, когда нужно соединить таблицу значений с данными из базы. Тогда загружайте её во временную таблицу через менеджер временных таблиц и соединяйте обычным
СОЕДИНЕНИЕ. - ⚠️ На маленьких таблицах (десятки строк) запрос только замедлит работу из-за накладных расходов. Здесь хватит цикла.
Составные типы, пустые значения и NULL
Здесь чаще всего и ломается отбор, хотя код выглядит правильно.
Пустое значение и Неопределено — не одно и то же
НайтиСтроки ищет строгое равенство, поэтому:
- пустая строка
"",Неопределено,Nullи пустая ссылка — четыре разных значения; - отбор по
Неопределеноне найдёт строки с пустой ссылкой на справочник.
Нужно найти «всё пустое» — используйте цикл и проверку ЗначениеЗаполнено():
Пустые = Новый Массив;
Для Каждого Строка Из Таблица Цикл
Если Не ЗначениеЗаполнено(Строка.Контрагент) Тогда
Пустые.Добавить(Строка);
КонецЕсли;
КонецЦикла;
ЗначениеЗаполнено считает пустыми и Неопределено, и Null, и пустую ссылку, и нулевое число, и пустую строку из пробелов. Если нуль или пробел для вас осмысленные значения, проверяйте точнее.
Колонка с несколькими типами
Если в колонке могут лежать и ссылки, и строки, и числа, сравнивать надо с учётом типа. Структура отбора сравнит значения как есть, а вот в цикле стоит сначала проверить тип:
Если ТипЗнч(Строка.Значение) = Тип("Число") И Строка.Значение > 100 Тогда
Подходящие.Добавить(Строка);
КонецЕсли;
Без проверки сравнение «строка больше числа» выдаст исключение.
NULL в запросе
Когда таблица-параметр попадает в запрос и вы соединяете её с чем-то левым соединением, несовпавшие поля придут как NULL. Условие Поле = &Значение для NULL всегда ложно. Проверяйте через ЕСТЬNULL(Поле, 0) или Поле ЕСТЬ NULL. Так же и для составных полей: чтобы условие по типу сработало, используйте ВЫРАЗИТЬ(Поле КАК Число) или ТИПЗНАЧЕНИЯ(Поле) = ТИП(Справочник.Контрагенты).
В ГДЕ нельзя полагаться на сравнение с NULL обычным знаком равенства. Поле = NULL не вернёт ни одной строки, даже если NULL там есть.
Скорость: индексы и лишние проходы
Фильтр по таблице на тысячи строк, который вызывается в цикле по другой таблице, быстро превращается в тормоз. Каждый НайтиСтроки без индекса перебирает таблицу целиком. Выйдет «строки умножить на строки».
Что помогает:
- Индекс по колонке отбора. Создаётся один раз перед поиском:
Таблица.Индексы.Добавить("Номенклатура");
Дальше НайтиСтроки по этой колонке работает заметно быстрее. Индекс стоит добавлять, когда поиск по таблице повторяется много раз. Для одного разового отбора он ничего не даст.
- Соответствие вместо поиска. Если нужно по ключу достать данные, один раз соберите
Соответствиеи обращайтесь к нему:
ПоКодам = Новый Соответствие;
Для Каждого Строка Из Таблица Цикл
ПоКодам.Вставить(Строка.Код, Строка);
КонецЦикла;
Нашли = ПоКодам.Получить("0000123");
Ограничение: на один ключ придётся одна строка, дубли перезапишутся.
- Сворачивание вместо фильтрации. Если вам нужны суммы по группам, не фильтруйте таблицу по каждому значению, а вызовите
Таблица.Свернуть("Номенклатура", "Количество, Сумма"). Этот метод меняет исходную таблицу, поэтому сначала сделайте копию.
Частые ошибки при отборе
Здесь только то, что не разобрано выше.
- ⚠️ Отбор по полю, которого нет. Колонку добавили в запрос или в процедуру, а в таблицу забыли. Ошибка про отсутствующее поле появится только при выполнении.
- ⚠️ Правка копии вместо оригинала. После
Скопироватьизменения не попадают в исходную таблицу. Нужно менять исходную — работайте со строками изНайтиСтроки. - ⚠️ Ссылка на строку после удаления. Если строку удалили, переменная, где она лежала, указывает на «мёртвую» строку. Обращение к её полям даст ошибку.
- ⚠️ Фильтр по табличной части документа. Табличная часть — это не таблица значений, хотя методы
НайтиСтрокииВыгрузитьу неё есть. Копия черезВыгрузить()становится обычной таблицей значений.
Таблица.Колонки.Неопределено и пустая ссылка не равны. Выведите значение и ТипЗнч из первой строки и сравните с искомым.Новый ОписаниеТипов(...).Для Каждого по той же таблице. Соберите массив строк и удаляйте после обхода или идите с конца.Частые вопросы
Можно ли в НайтиСтроки задать условие «больше» или «не равно»?
ГДЕ.Меняет ли НайтиСтроки исходную таблицу?
Как отфильтровать по нескольким значениям одной колонки («или»)?
НайтиСтроки отдельно для каждого значения и сложите результаты в один массив, либо используйте цикл с проверкой по списку, либо запрос с условием В (&Список).Как отфильтровать табличную часть документа?
Выгрузить() и работайте как с обычной таблицей. Менять нужно саму табличную часть — применяйте НайтиСтроки к ней напрямую.Чем отличается Найти от НайтиСтроки?
Найти возвращает первую строку или Неопределено, НайтиСтроки — массив всех подходящих строк.