В практике разработки на платформе 1С:Предприятие 8 часто возникает задача поиска значения по так называемой «шкале» или «вилке». Типичный пример: расчет процента скидки в зависимости от количества товара, расчет надбавки за стаж или определение налоговой ставки. Особенность задачи заключается в том, что искомое число (например, количество) редко совпадает с эталонным значением в таблице, и нам нужно найти ближайшую нижнюю границу диапазона.
Рассмотрим ситуацию на примере: у нас есть таблица со значениями 26, 30, 35, 40 и соответствующими им процентами. Если входное значение равно 32, программа должна вернуть процент, соответствующий числу 30. Разберем наиболее эффективные способы решения этой задачи.
Это наиболее универсальный и «чистый» с точки зрения архитектуры 1С способ, особенно если таблица значений (ТЗ) уже содержит много данных или если результат нужно встроить в более сложную выборку. Основная логика заключается в фильтрации данных, которые меньше или равны нашему числу, и выборе самого первого (максимального из оставшихся) значения.
Рассмотрим пошагово реализацию через запрос:
ГДЕ Количество <= &ЗаданноеЗначение.ВЫБРАТЬ ПЕРВЫЕ 1).Проанализируем пример кода:
// Предположим, ТЗ_Шкала уже заполнена колонками "Количество" и "Процент"
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ВТ_Шкала.Процент КАК Процент
|ИЗ
| &ТЗ_Шкала КАК ВТ_Шкала
|ГДЕ
| ВТ_Шкала.Количество <= &НужноеКоличество
|
|УПОРЯДОЧИТЬ ПО
| ВТ_Шкала.Количество УБЫВ";
Запрос.УстановитьПараметр("ТЗ_Шкала", ТЗ_Шкала);
Запрос.УстановитьПараметр("НужноеКоличество", 32);
РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Если Выборка.Следующий() Тогда
НужныйПроцент = Выборка.Процент;
Иначе
// Обработка случая, если значение меньше самого минимального в таблице
НужныйПроцент = 0;
КонецЕсли;
Важное примечание: При передаче таблицы значений в запрос 1С автоматически создает временную таблицу в СУБД. Чтобы это работало корректно, колонки в ТаблицаЗначений должны быть строго типизированы (например, через ОписаниеТипов при создании таблицы).
Если ваша таблица содержит небольшое количество строк (например, до 50–100), использование запроса может быть избыточным из-за накладных расходов на создание временных таблиц в SQL. В этом случае проще и быстрее применить обычный перебор в цикле.
Проанализируем ситуацию: чтобы найти нижнюю границу, нам нужно сравнить каждое значение с искомым. Существует два варианта реализации цикла: для отсортированной и неотсортированной таблицы.
Вариант для произвольной таблицы:
МаксКоличество = -1;
НужныйПроцент = 0;
ИскомоеЧисло = 32;
Для Каждого СтрокаШкалы Из ТЗ_Шкала Цикл
Если СтрокаШкалы.Количество <= ИскомоеЧисло Тогда
// Проверяем, является ли это значение самым близким из найденных
Если СтрокаШкалы.Количество > МаксКоличество Тогда
МаксКоличество = СтрокаШкалы.Количество;
НужныйПроцент = СтрокаШкалы.Процент;
КонецЕсли;
КонецЕсли;
КонецЦикла;
Вариант для предварительно отсортированной таблицы:
Если мы заранее выполним ТЗ_Шкала.Сортировать("Количество УБЫВ"), то первое же встреченное значение, которое меньше или равно искомому, и будет нашей целью. Это позволяет прервать цикл немедленно (Прервать), что экономит ресурсы.
Выясним причину, почему в высоконагруженных системах не используют простой цикл. Если в таблице тысячи строк, перебор будет медленным. В таких случаях мы используем метод дихотомии (бинарный поиск).
Разберем алгоритм: мы делим отсортированный массив пополам и проверяем, в какой части находится наше число. Повторяем процедуру до тех пор, пока не найдем границы диапазона. В 1С это реализуется вручную:
Лево = 0;
Право = ТЗ_Шкала.Количество() - 1;
РезультатИндекс = -1;
Пока Лево <= Право Цикл
Среднее = Цел((Лево + Право) / 2);
Если ТЗ_Шкала[Среднее].Количество <= ИскомоеЧисло Тогда
РезультатИндекс = Среднее;
Лево = Среднее + 1;
Иначе
Право = Среднее - 1;
КонецЕсли;
КонецЦикла;
Если РезультатИндекс <> -1 Тогда
Процент = ТЗ_Шкала[РезультатИндекс].Процент;
КонецЕсли;
При решении задачи «поиска в вилке» следует учитывать несколько важных моментов:
ТаблицаЗначений совпадали с типами параметров запроса. Если в ТЗ колонка имеет неопределенный тип, запрос может вернуть ошибку.ИндексированнаяТаблицаЗначений. Это значительно ускорит работу методов поиска и запросов.Таким образом, мы рассмотрели основные способы получения минимального значения относительно заданного. Выбор конкретного инструмента зависит от объема ваших данных и требований к производительности системы. Помните, что код должен быть не только быстрым, но и понятным для других разработчиков.