Как правильно найти значение в интервале в запросе 1С и избежать дублирования границ?

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

При разработке систем лояльности, расчете скидок или определении тарифов программисты 1С часто сталкиваются с необходимостью определить, в какой числовой или временной интервал попадает конкретное значение. Стандартный подход с использованием оператора МЕЖДУ (или BETWEEN в SQL) кажется очевидным, но он таит в себе подводный камень, который может привести к неверным результатам и дублированию строк. Рассмотрим подробнее, почему это происходит и как реализовать поиск по интервалам правильно.

Анализ проблемы: почему оператор МЕЖДУ не всегда подходит

Разберем ситуацию, описанную в теме. В регистре сведений хранятся пороги скидок: от 20 000 до 50 000 (5%) и от 50 000 до 80 000 (6%). Проблема заключается в том, что в языке запросов 1С оператор МЕЖДУ всегда включает обе границы интервала.

Если мы напишем условие:

ГДЕ &СуммаНакопления МЕЖДУ ЗначенияСкидок.От И ЗначенияСкидок.До

то при значении параметра СуммаНакопления равном ровно 50 000, система вернет обе записи. Это происходит потому, что 50 000 одновременно «больше или равно 20 000 и меньше или равно 50 000» (первая запись) и «больше или равно 50 000 и меньше или равно 80 000» (вторая запись). Для корректной работы алгоритма нам нужно, чтобы значение попадало только в один из интервалов.

Решение №1: Использование строгих неравенств

Самый простой и эффективный способ исправить логику — отказаться от оператора МЕЖДУ в пользу комбинации условий со строгим и нестрогим сравнением. Это позволит сделать одну из границ интервала «открытой». Проанализируем, как изменится текст запроса:


ВЫБРАТЬ
    ЗначенияСкидок.ПроцентСкидки КАК ПроцентСкидки
ИЗ
    РегистрСведений.ЗначенияСкидок КАК ЗначенияСкидок
ГДЕ
    &СуммаНакопления >= ЗначенияСкидок.От 
    И &СуммаНакопления < ЗначенияСкидок.До

В данном примере мы включили нижнюю границу (>=) и исключили верхнюю (<). Теперь значение 50 000 не попадет в первый интервал, так как условие «50 000 меньше 50 000» ложно, но успешно попадет во второй. Этот метод считается классическим при работе с непрерывными шкалами.

Решение №2: Методика поиска по одной границе (Top 1)

Существует еще более надежный архитектурный подход, который часто применяется в типовых конфигурациях 1С (например, в 1С:ERP или УТ 11). Мы можем хранить в регистре только нижнюю границу диапазона (поле От). Это избавляет нас от необходимости синхронизировать верхнюю границу предыдущей записи с нижней границей последующей.

Давайте выясним, как построить запрос в этом случае. Нам нужно найти ближайший «порог», который меньше или равен нашей сумме, и взять самую первую такую запись при сортировке по убыванию:


ВЫБРАТЬ ПЕРВЫЕ 1
    ЗначенияСкидок.ПроцентСкидки КАК ПроцентСкидки
ИЗ
    РегистрСведений.ЗначенияСкидок КАК ЗначенияСкидок
ГДЕ
    ЗначенияСкидок.От <= &СуммаНакопления
УПОРЯДОЧИТЬ ПО
    ЗначенияСкидок.От УБЫВ

Рассмотрим пример: у нас есть пороги 0 (скидка 3%), 10 000 (5%) и 50 000 (10%).

  1. Если сумма 5 000: запрос найдет записи, где От <= 5000 (это только 0), и вернет 3%.
  2. Если сумма 45 000: запрос найдет записи с От равным 0 и 10 000. Благодаря сортировке УБЫВ и ПЕРВЫЕ 1, система выберет 10 000 и вернет 5%.

Этот метод наиболее устойчив к ошибкам заполнения данных, так как исключает саму возможность появления «дырок» или перекрытий в интервалах.

Решение №3: Обработка неограниченных и пустых интервалов

Часто в шкалах последний интервал не имеет верхней границы (например, «от 100 000 и выше»). Если вы используете структуру с двумя колонками (От и До), в поле До может оказаться 0 или пустое значение. В этом случае МЕЖДУ или обычное сравнение перестанут работать.

Проанализируем ситуацию: если мы ищем значение 150 000, а в базе последняя запись — «От 100 000, До 0», условие 150 000 <= 0 вернет ложь. Чтобы этого избежать, используйте функцию ВЫБОР или ЕСТЬNULL для подстановки бесконечно большого числа:


ГДЕ
    &СуммаНакопления >= ЗначенияСкидок.От 
    И (&СуммаНакопления < ЗначенияСкидок.До ИЛИ ЗначенияСкидок.До = 0)

Или через подстановку константы в ВЫБОР:


ГДЕ
    &СуммаНакопления >= ЗначенияСкидок.От 
    И &СуммаНакопления < ВЫБОР 
        КОГДА ЗначенияСкидок.До = 0 ТОГДА 9999999999 
        ИНАЧЕ ЗначенияСкидок.До 
    КОНЕЦ

Важные замечания по типам данных и точности

При работе с интервалами крайне важно учитывать специфику типов данных в 1С:

  1. Тип "Дата": Оператор МЕЖДУ для дат включает время. Если интервал заканчивается датой 31.12.2023, это означает 31.12.2023 00:00:00. Любой документ от этой же даты, но со временем 12:00:00, формально окажется вне интервала, если сравнивать его с концом дня через МЕЖДУ. Поэтому для дат всегда лучше использовать КОНЕЦПЕРИОДА или строгое сравнение < ДобавитьКДате(&ДатаКонца, "Секунда", 1).
  2. Числа с плавающей точкой: Если суммы имеют копейки, следите за округлением. Разница в одну миллионную долю (например, из-за особенностей хранения в SQL) может привести к тому, что значение «провалится» между интервалами.

Рекомендации по контролю данных

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

Система должна проверять:

Использование метода Top 1 (Решение №2) автоматически снимает большинство этих проблем, поэтому мы рекомендуем его как наиболее надежный стандарт разработки в среде 1С:Предприятие.

← На главную