Как вычесть диапазоны чисел из исходного диапазона в запросе 1С?

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

Часто при разработке учетных систем, систем бронирования или распределения номерных фондов возникает задача: есть исходный диапазон (например, номера от 1 до 99) и набор поддиапазонов, которые нужно исключить. В языке запросов 1С задача осложняется отсутствием прямых функций для работы с интервалами, поэтому мы разберем надежный и эффективный способ реализации этой логики с помощью временных таблиц.

Подготовительный этап: структура данных

Для начала давайте определимся с входными данными. У нас есть две сущности: исходный диапазон и список диапазонов-исключений. Чтобы наш запрос работал универсально, представим их как временные таблицы. Предположим, что вычитаемые диапазоны не пересекаются друг с другом.

Создадим наши таблицы:


ВЫБРАТЬ
    1 КАК Начало,
    99 КАК Конец
ПОМЕСТИТЬ Полный
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    15 КАК Начало,
    30 КАК Конец
ПОМЕСТИТЬ Исключение
 ОБЪЕДИНИТЬ ВСЕ
 ВЫБРАТЬ
    47,
    52
;

Теперь, когда данные подготовлены, перейдем к самому алгоритму вычитания.

Алгоритм решения через точки разрыва

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

Разберем процесс по шагам:

  1. Формируем список новых начал. Началом нового интервала может быть либо начало исходного отрезка, либо точка сразу после завершения исключаемого интервала.
  2. Формируем список новых концов. Концом нового интервала может быть либо конец исходного отрезка, либо точка перед началом исключаемого интервала.
  3. Объединяем данные. Используем ЛЕВОЕ СОЕДИНЕНИЕ для сопоставления начал и ближайших подходящих концов.

Реализуем это в коде запроса:


ВЫБРАТЬ РАЗЛИЧНЫЕ
    Исключение.Конец + 1 КАК Начало
ПОМЕСТИТЬ ВТ_НачалаОтрезков
ИЗ
    Исключение КАК Исключение
 ОБЪЕДИНИТЬ
 ВЫБРАТЬ
    Полный.Начало
ИЗ
    Полный КАК Полный
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Исключение.Начало - 1 КАК Конец
ПОМЕСТИТЬ ВТ_КонцыОтрезков
ИЗ
    Исключение КАК Исключение
 ОБЪЕДИНИТЬ
 ВЫБРАТЬ РАЗЛИЧНЫЕ
    Полный.Конец
ИЗ
    Полный КАК Полный
;

Финальная сборка и фильтрация

Теперь нам осталось соединить полученные начала и концы, чтобы сформировать результирующую таблицу. Важно правильно отсечь «мусорные» интервалы, которые могут возникнуть при некорректном сопоставлении точек.


ВЫБРАТЬ
    ВТ_НачалаОтрезков.Начало КАК Начало,
    МИНИМУМ(ВТ_КонцыОтрезков.Конец) КАК Конец
ПОМЕСТИТЬ ВТ_НаложениеОтрезков
ИЗ
    ВТ_НачалаОтрезков КАК ВТ_НачалаОтрезков
        ЛЕВОЕ СОЕДИНЕНИЕ ВТ_КонцыОтрезков КАК ВТ_КонцыОтрезков
        ПО ВТ_НачалаОтрезков.Начало <= ВТ_КонцыОтрезков.Конец
 СГРУППИРОВАТЬ ПО
    ВТ_НачалаОтрезков.Начало
;
////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ_НаложениеОтрезков.Начало КАК Начало,
    ВТ_НаложениеОтрезков.Конец КАК Конец
ИЗ
    ВТ_НаложениеОтрезков КАК ВТ_НаложениеОтрезков
ГДЕ
    ВТ_НаложениеОтрезков.Начало <= ВТ_НаложениеОтрезков.Конец
УПОРЯДОЧИТЬ ПО
    Начало

Почему этот метод эффективен?

Рассмотренный подход имеет ряд преимуществ:

О чем стоит помнить?

При работе с диапазонами всегда учитывайте граничные случаи. Важно проверять, чтобы начало диапазона всегда было меньше или равно его концу. Если в данных встречаются ситуации, когда исключаемый интервал выходит за границы основного, результат запроса может потребовать дополнительной фильтрации с помощью условия ГДЕ, как мы сделали на финальном этапе. Всегда проверяйте результат на пустых выборках — использование функций ЕСТЬNULL поможет сделать алгоритм более устойчивым.

Этот метод является классическим решением, которое можно адаптировать под практически любые задачи, где требуется манипуляция числовыми интервалами в 1С.

← На главную