Как найти минимальное значение больше нуля в табличной части?

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

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

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

Способ 1. Получение значения с помощью запроса к табличной части в памяти

Если данные редактируются пользователем на форме и объект еще не записан в базу данных, прямой запрос к таблице базы вернет либо пустой результат, либо устаревшие сведения. Чтобы обработать актуальные несохраненные данные, выгрузим табличную часть в объект ТаблицаЗначений и передадим ее параметром в запрос.

Рассмотрим подробнее пример кода на сервере:


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

Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   ЕСТЬNULL(МИНИМУМ(ТЗ.Цена), 0) КАК МинЦена
    |ИЗ
    |   &ТаблицаДляЗапроса КАК ТЗ
    |ГДЕ
    |   ТЗ.Цена > 0";

Запрос.УстановитьПараметр("ТаблицаДляЗапроса", ТаблицаДляЗапроса);
Выборка = Запрос.Выполнить().Выбрать();

Если Выборка.Следующий() Тогда
    МинимальнаяЦена = Выборка.МинЦена;
КонецЕсли;

Обратим внимание на важный нюанс: если в табличной части нет ни одной строки со значением больше нуля, функция МИНИМУМ() вернет значение NULL. Использование функции языка запросов ЕСТЬNULL() позволяет избежать непредвиденных ошибок при дальнейшей обработке результата и получить гарантированное числовое значение по умолчанию.

Способ 2. Запрос к записанным данным в базе данных

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

Посмотрим на пример запроса для сохраненного документа:


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

Запрос.УстановитьПараметр("Ссылка", СсылкаНаДокумент);
Выборка = Запрос.Выполнить().Выбрать();

Если Выборка.Следующий() Тогда
    МинимальнаяЦена = Выборка.МинЦена;
КонецЕсли;

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

Способ 3. Универсальный перебор строк в цикле

В управляемых формах на стороне клиента коллекция строк представлена объектом ДанныеФормыКоллекция. Выполнять запросы на клиенте нельзя, а лишний контекстный вызов сервера ради одной операции может снизить отзывчивость интерфейса. В таком случае наиболее простым и быстродействующим решением является обход строк в цикле.

Разберем алгоритм поиска:

  1. Инициализируем переменную для хранения минимума значением Неопределено.
  2. Проходим в цикле по всем строкам табличной части.
  3. Проверяем, что значение в текущей строке строго больше нуля.
  4. Если переменная минимума еще не заполнена или текущее значение меньше уже найденного, сохраняем новое значение.

Посмотрим на реализацию алгоритма:


МинЗначение = Неопределено;

Для Каждого Стр Из Объект.Товары Цикл
    Если Стр.Цена > 0 Тогда
        Если МинЗначение = Неопределено Или Стр.Цена < МинЗначение Тогда
            МинЗначение = Стр.Цена;
        КонецЕсли;
    КонецЕсли;
КонецЦикла;

Если МинЗначение = Неопределено Тогда
    // Значения больше нуля в таблице отсутствуют
    МинЗначение = 0;
КонецЕсли;

Данный подход не создает дополнительных временных объектов и одинаково корректно работает как в тонком клиенте, так и на сервере.

Способ 4. Фильтрация и сортировка таблицы значений

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

Проанализируем пример:


ТЗ = Объект.Товары.Выгрузить();

// Удаляем неподходящие строки с конца таблицы
Индекс = ТЗ.Количество() - 1;
Пока Индекс >= 0 Цикл
    Если ТЗ[Индекс].Цена <= 0 Тогда
        ТЗ.Удалить(Индекс);
    КонецЕсли;
    Индекс = Индекс - 1;
КонецЦикла;

Если ТЗ.Количество() > 0 Тогда
    ТЗ.Сортировать("Цена Возр");
    МинимальнаяЦена = ТЗ[0].Цена;
Иначе
    МинимальнаяЦена = 0;
КонецЕсли;

После сортировки по возрастанию первое значение отфильтрованной таблицы гарантированно будет минимальным положительным числом.

← На главную