Как в запросе 1С выбрать временный маршрут с приоритетом над постоянным?

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

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

Рассмотрим, как правильно составить такой запрос на языке 1С, проанализируем возможные подводные камни при работе с датами и разберем альтернативные подходы к реализации.

Исходные данные задачи

Представим, что у нас есть две структуры данных:

  1. Таблица заказов: содержит поля Заказ, Контрагент, ДатаОтгрузки.
  2. Таблица маршрутов: содержит поля Маршрут, Контрагент, ДатаНачала, ДатаОкончания. Постоянный маршрут характеризуется незаполненной датой окончания (ДатаОкончания = ДАТАВРЕМЯ(1, 1, 1)), а временный — заполненным интервалом действия.

Вариант 1. Раздельное левое соединение с функцией ЕСТЬNULL

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

  1. Первое соединение — с временными маршрутами, где дата отгрузки попадает в закрытый интервал.
  2. Второе соединение — с постоянными маршрутами, где проверяется только нижняя граница даты и пустая дата окончания.

В итоговой выборке используем конструкцию ЕСТЬNULL(Временные.Маршрут, Постоянные.Маршрут). Если временный маршрут найден, функция возвратит его; если соединение с временным маршрутом вернуло NULL, будет подставлен постоянный маршрут.

Посмотрим на пример текста запроса:


ВЫБРАТЬ
    Заказы.Заказ КАК Заказ,
    Заказы.Контрагент КАК Контрагент,
    Заказы.ДатаОтгрузки КАК ДатаОтгрузки,
    ЕСТЬNULL(ВременныеМаршруты.Маршрут, ПостоянныеМаршруты.Маршрут) КАК Маршрут
ИЗ
    ТаблицаЗаказов КАК Заказы
    
    // 1. Соединение с временными маршрутами (приоритетные)
    ЛЕВОЕ СОЕДИНЕНИЕ ТаблицаМаршрутов КАК ВременныеМаршруты
        ПО Заказы.Контрагент = ВременныеМаршруты.Контрагент
        И Заказы.ДатаОтгрузки >= ВременныеМаршруты.ДатаНачала
        И Заказы.ДатаОтгрузки <= ВременныеМаршруты.ДатаОкончания
        И ВременныеМаршруты.ДатаОкончания <> ДАТАВРЕМЯ(1, 1, 1)
        
    // 2. Соединение с постоянными маршрутами (базовые)
    ЛЕВОЕ СОЕДИНЕНИЕ ТаблицаМаршрутов КАК ПостоянныеМаршруты
        ПО Заказы.Контрагент = ПостоянныеМаршруты.Контрагент
        И Заказы.ДатаОтгрузки >= ПостоянныеМаршруты.ДатаНачала
        И ПостоянныеМаршруты.ДатаОкончания = ДАТАВРЕМЯ(1, 1, 1)

Вариант 2. Вычисление числового приоритета с группировкой

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

Разберем по шагам логику этого алгоритма:

  1. Выполняем общее левое соединение заказов со всеми подходящими по датам маршрутами.
  2. С помощью оператора ВЫБОР назначаем числовой вес: временному маршруту присваиваем значение 2, постоянному — 1.
  3. Во временной таблице группируем данные по заказу и вычисляем МАКСИМУМ(Приоритет).
  4. Итоговым запросом соединяем исходные данные по максимальному приоритету и получаем единственный нужный маршрут.

Посмотрим на реализацию запроса с пакетом временных таблиц:


ВЫБРАТЬ
    Заказы.Заказ КАК Заказ,
    Заказы.Контрагент КАК Контрагент,
    Заказы.ДатаОтгрузки КАК ДатаОтгрузки,
    Маршруты.Маршрут КАК Маршрут,
    ВЫБОР
        КОГДА Маршруты.ДатаОкончания <> ДАТАВРЕМЯ(1, 1, 1) ТОГДА 2
        ИНАЧЕ 1
    КОНЕЦ КАК Приоритет
ПОМЕСТИТЬ ВТ_ВсеПодходящиеМаршруты
ИЗ
    ТаблицаЗаказов КАК Заказы
    ЛЕВОЕ СОЕДИНЕНИЕ ТаблицаМаршрутов КАК Маршруты
        ПО Заказы.Контрагент = Маршруты.Контрагент
        И Заказы.ДатаОтгрузки >= Маршруты.ДатаНачала
        И (Заказы.ДатаОтгрузки <= Маршруты.ДатаОкончания 
            ИЛИ Маршруты.ДатаОкончания = ДАТАВРЕМЯ(1, 1, 1))
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ.Заказ КАК Заказ,
    МАКСИМУМ(ВТ.Приоритет) КАК МаксПриоритет
ПОМЕСТИТЬ ВТ_МаксимальныеПриоритеты
ИЗ
    ВТ_ВсеПодходящиеМаршруты КАК ВТ
СГРУППИРОВАТЬ ПО
    ВТ.Заказ
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ_Приоритеты.Заказ КАК Заказ,
    ВТ_Маршруты.Контрагент КАК Контрагент,
    ВТ_Маршруты.ДатаОтгрузки КАК ДатаОтгрузки,
    ВТ_Маршруты.Маршрут КАК Маршрут
ИЗ
    ВТ_МаксимальныеПриоритеты КАК ВТ_Приоритеты
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ВсеПодходящиеМаршруты КАК ВТ_Маршруты
        ПО ВТ_Приоритеты.Заказ = ВТ_Маршруты.Заказ
        И ВТ_Приоритеты.МаксПриоритет = ВТ_Маршруты.Приоритет

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

При работе с интервалами дат в запросах 1С необходимо учитывать следующие детали:

  1. Учет времени в датах: В платформе 1С тип Дата всегда содержит время. Если ДатаОкончания временного маршрута записана без времени (например, 30.05.2017 00:00:00), а ДатаОтгрузки содержит время документа (например, 30.05.2017 15:30:00), то проверка ДатаОтгрузки <= ДатаОкончания вернет ЛОЖЬ. Чтобы избежать ошибки, приводите даты к границам дней с помощью функций НАЧАЛОПЕРИОДА(Заказы.ДатаОтгрузки, ДЕНЬ) и КОНЕЦПЕРИОДА(Маршруты.ДатаОкончания, ДЕНЬ).
  2. Индексация временных таблиц: Соединение по неравенствам дат — ресурсоемкая операция для СУБД. Если таблицы заказов и маршрутов передаются в запрос через менеджер временных таблиц из ТаблицаЗначений, обязательно используйте директиву ИНДЕКСИРОВАТЬ ПО по полям связи:
    
    ПОМЕСТИТЬ ТаблицаЗаказов
    ИНДЕКСИРОВАТЬ ПО
        Контрагент,
        ДатаОтгрузки
    ;
    
  3. Архитектурная альтернатива: Если есть возможность изменить структуру хранения данных в конфигурации, хранение интервалов «Дата начала — Дата окончания» эффективнее заменить на периодический Регистр сведений с периодичностью по дням. В таком случае получение актуального маршрута на любую дату отгрузки сводится к стандартной виртуальной таблице СрезПоследних, которая работает на уровне СУБД быстрее и исключает громоздкие интервальные соединения.
← На главную