Как из таблицы значений получить минимальное значение относительно заданного?

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

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

Рассмотрим ситуацию на примере: у нас есть таблица со значениями 26, 30, 35, 40 и соответствующими им процентами. Если входное значение равно 32, программа должна вернуть процент, соответствующий числу 30. Разберем наиболее эффективные способы решения этой задачи.

Способ 1: Использование запроса к таблице значений

Это наиболее универсальный и «чистый» с точки зрения архитектуры 1С способ, особенно если таблица значений (ТЗ) уже содержит много данных или если результат нужно встроить в более сложную выборку. Основная логика заключается в фильтрации данных, которые меньше или равны нашему числу, и выборе самого первого (максимального из оставшихся) значения.

Рассмотрим пошагово реализацию через запрос:

  1. Подготовим таблицу значений и передадим ее в параметры запроса.
  2. В тексте запроса выберем данные из временной таблицы.
  3. Установим условие ГДЕ Количество <= &ЗаданноеЗначение.
  4. Упорядочим результат по убыванию количества.
  5. Возьмем только первую строку (ВЫБРАТЬ ПЕРВЫЕ 1).

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


// Предположим, ТЗ_Шкала уже заполнена колонками "Количество" и "Процент"
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ВТ_Шкала.Процент КАК Процент
    |ИЗ
    |	&ТЗ_Шкала КАК ВТ_Шкала
    |ГДЕ
    |	ВТ_Шкала.Количество <= &НужноеКоличество
    |
    |УПОРЯДОЧИТЬ ПО
    |	ВТ_Шкала.Количество УБЫВ";

Запрос.УстановитьПараметр("ТЗ_Шкала", ТЗ_Шкала);
Запрос.УстановитьПараметр("НужноеКоличество", 32);

РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();

Если Выборка.Следующий() Тогда
    НужныйПроцент = Выборка.Процент;
Иначе
    // Обработка случая, если значение меньше самого минимального в таблице
    НужныйПроцент = 0; 
КонецЕсли;

Важное примечание: При передаче таблицы значений в запрос 1С автоматически создает временную таблицу в СУБД. Чтобы это работало корректно, колонки в ТаблицаЗначений должны быть строго типизированы (например, через ОписаниеТипов при создании таблицы).

Способ 2: Поиск через цикл (для небольших таблиц)

Если ваша таблица содержит небольшое количество строк (например, до 50–100), использование запроса может быть избыточным из-за накладных расходов на создание временных таблиц в SQL. В этом случае проще и быстрее применить обычный перебор в цикле.

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

Вариант для произвольной таблицы:


МаксКоличество = -1;
НужныйПроцент = 0;
ИскомоеЧисло = 32;

Для Каждого СтрокаШкалы Из ТЗ_Шкала Цикл
    Если СтрокаШкалы.Количество <= ИскомоеЧисло Тогда
        // Проверяем, является ли это значение самым близким из найденных
        Если СтрокаШкалы.Количество > МаксКоличество Тогда
            МаксКоличество = СтрокаШкалы.Количество;
            НужныйПроцент = СтрокаШкалы.Процент;
        КонецЕсли;
    КонецЕсли;
КонецЦикла;

Вариант для предварительно отсортированной таблицы:

Если мы заранее выполним ТЗ_Шкала.Сортировать("Количество УБЫВ"), то первое же встреченное значение, которое меньше или равно искомому, и будет нашей целью. Это позволяет прервать цикл немедленно (Прервать), что экономит ресурсы.

Способ 3: Бинарный поиск (для больших массивов данных)

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

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


Лево = 0;
Право = ТЗ_Шкала.Количество() - 1;
РезультатИндекс = -1;

Пока Лево <= Право Цикл
    Среднее = Цел((Лево + Право) / 2);
    Если ТЗ_Шкала[Среднее].Количество <= ИскомоеЧисло Тогда
        РезультатИндекс = Среднее;
        Лево = Среднее + 1;
    Иначе
        Право = Среднее - 1;
    КонецЕсли;
КонецЦикла;

Если РезультатИндекс <> -1 Тогда
    Процент = ТЗ_Шкала[РезультатИндекс].Процент;
КонецЕсли;

Технические нюансы и рекомендации

При решении задачи «поиска в вилке» следует учитывать несколько важных моментов:

  1. Типизация данных: Если вы используете метод запроса, всегда проверяйте, чтобы типы колонок в ТаблицаЗначений совпадали с типами параметров запроса. Если в ТЗ колонка имеет неопределенный тип, запрос может вернуть ошибку.
  2. Индексация: Если вы планируете часто искать данные в одной и той же таблице значений, используйте ИндексированнаяТаблицаЗначений. Это значительно ускорит работу методов поиска и запросов.
  3. Граничные условия (Edge Cases): Всегда предусматривайте поведение программы, если введенное число меньше самого первого значения в шкале. Например, если шкала начинается с 10, а введено 5. В таких случаях принято либо возвращать 0, либо брать значение первой строки.
  4. Выбор метода:
    • Для таблиц до 30–50 строк — используйте цикл.
    • Для встраивания в сложные алгоритмы расчета — запрос.
    • Для огромных справочников или массивов — бинарный поиск.

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

← На главную