Как в запросе 1С определить, находится ли транспортное средство на маршруте по точкам входа и выхода?

Программист
← На главную

В задачах транспортной логистики и GPS-мониторинга часто возникает необходимость разметить сырые данные о посещении геозон. Представим ситуацию: у нас есть таблица с событиями входа и выхода транспортного средства (ТС) из различных зон. Среди этих зон есть контрольные точки — Начальная и Конечная. Наша задача — пометить все записи, которые хронологически находятся между входом в начальную точку и выходом из конечной, статусом "На маршруте", а остальные — "Вне маршрута".

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

Метод 1: Использование временных таблиц для определения границ периода

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

Сначала мы подготовим исходные данные и выделим моменты времени, соответствующие началу и концу маршрута. Для этого воспользуемся конструкцией ВЫБОР КОГДА.


// Шаг 1: Подготовка данных и выделение меток времени
ВЫБРАТЬ
    ИсходныеДанные.ВходЗону КАК ВходЗону,
    ИсходныеДанные.ВыходИзЗоны КАК ВыходИзЗоны,
    ИсходныеДанные.ТС КАК ТС,
    ИсходныеДанные.Зона КАК Зона,
    ИсходныеДанные.НачальнаяТочка КАК НачальнаяТочка,
    ИсходныеДанные.КонечнаяТочка КАК КонечнаяТочка,
    ВЫБОР КОГДА ИсходныеДанные.НачальнаяТочка ТОГДА ИсходныеДанные.ВходЗону ИНАЧЕ NULL КОНЕЦ КАК ВремяНачала,
    ВЫБОР КОГДА ИсходныеДанные.КонечнаяТочка ТОГДА ИсходныеДанные.ВыходИзЗоны ИНАЧЕ NULL КОНЕЦ КАК ВремяОкончания
ПОМЕСТИТЬ вт_ДанныеСМетками
ИЗ
    &ТаблицаДанных КАК ИсходныеДанные;

Теперь проанализируем полученную таблицу. В полях ВремяНачала и ВремяОкончания значения будут заполнены только для тех строк, которые являются контрольными точками. На следующем этапе мы "схлопнем" эти данные, чтобы получить одну строку с общими границами для ТС.


// Шаг 2: Определение единого интервала маршрута
ВЫБРАТЬ
    вт_ДанныеСМетками.ТС КАК ТС,
    МИНИМУМ(вт_ДанныеСМетками.ВремяНачала) КАК ОбщееНачало,
    МАКСИМУМ(вт_ДанныеСМетками.ВремяОкончания) КАК ОбщееОкончание
ПОМЕСТИТЬ вт_ПериодыМаршрута
ИЗ
    вт_ДанныеСМетками КАК вт_ДанныеСМетками
СГРУППИРОВАТЬ ПО
    вт_ДанныеСМетками.ТС;

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


// Шаг 3: Финальная разметка данных
ВЫБРАТЬ
    Данные.ВходЗону,
    Данные.ВыходИзЗоны,
    Данные.ТС,
    Данные.Зона,
    ВЫБОР
        КОГДА Данные.ВходЗону >= Периоды.ОбщееНачало 
              И Данные.ВыходИзЗоны <= Периоды.ОбщееОкончание 
        ТОГДА "На маршруте"
        ИНАЧЕ "Вне маршрута"
    КОНЕЦ КАК Статус
ИЗ
    вт_ДанныеСМетками КАК Данные
ЛЕВОЕ СОЕДИНЕНИЕ вт_ПериодыМаршрута КАК Периоды ПО Данные.ТС = Периоды.ТС;

Метод 2: Работа с несколькими рейсами (метод ближайших границ)

Рассмотрим более сложную ситуацию. Что если ТС за один день совершило несколько рейсов? Предыдущий метод найдет самое первое начало и самый последний конец, в результате чего все стоянки между рейсами ошибочно получат статус "На маршруте". Выясним, как этого избежать.

Для корректной работы с множеством интервалов нам нужно для каждой строки найти ближайшее предшествующее событие начала и ближайшее последующее событие конца. Это реализуется через соединение таблицы самой с собой по условию неравенства времени.

Алгоритм действий будет следующим:

  1. Создадим временную таблицу вт_Начала, где соберем все моменты НачальнаяТочка = Истина.
  2. Создадим временную таблицу вт_Концы, где соберем все моменты КонечнаяТочка = Истина.
  3. Для каждой записи из общего списка найдем такое ВремяНачала, которое является максимальным из всех, что меньше времени текущей записи.
  4. Аналогично найдем минимальное ВремяОкончания, которое больше времени текущей записи.
  5. Если текущая запись попадает в этот найденный "замок", значит она на маршруте.

Посмотрим на пример логики соединения для поиска границ:


ВЫБРАТЬ
    Т1.ТС,
    Т1.ВходЗону,
    МАКСИМУМ(Т_Старт.ВходЗону) КАК ВремяБлижайшегоСтарта
ПОМЕСТИТЬ вт_СвязкаСтарт
ИЗ
    вт_Данные КАК Т1
ЛЕВОЕ СОЕДИНЕНИЕ вт_Данные КАК Т_Старт 
    ПО Т1.ТС = Т_Старт.ТС 
    И Т_Старт.НачальнаяТочка = ИСТИНА
    И Т_Старт.ВходЗону <= Т1.ВходЗону
СГРУППИРОВАТЬ ПО
    Т1.ТС, Т1.ВходЗону;

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

Проанализируем несколько критических моментов, которые могут повлиять на точность и скорость работы запроса:

  1. Индексация временных таблиц: Если в вашей таблице более нескольких сотен строк, обязательно используйте индексацию. При соединении таблиц (особенно левых соединениях с условиями "больше-меньше") отсутствие индекса по полям ТС и ВходЗону приведет к колоссальным нагрузкам на СУБД. Используйте ИНДЕКСИРОВАТЬ ПО в описании временных таблиц.
  2. Пустые даты: В 1С пустая дата ДАТАВРЕМЯ(1,1,1) при сравнении ведет себя как самое минимальное значение. Если маршрут начат, но еще не завершен (нет записи о конечной точке), поле ВыходИзЗоны может быть пустым. В таких случаях мы рекомендуем использовать функцию ЕСТЬNULL или ВЫБОР, заменяя пустую дату на ДАТАВРЕМЯ(3999,12,31) для корректного сравнения.
  3. Пересекающиеся зоны: Иногда геозоны могут накладываться друг на друга. Проверьте, чтобы условие входа/выхода учитывало именно интервалы. Универсальная формула пересечения двух периодов: Начало1 <= Конец2 И Начало2 <= Конец1.

Использование СКД для решения задачи

Если ваша задача — построить отчет, то вместо сложного запроса можно воспользоваться Связями наборов данных в Системе компоновки данных (СКД).

Мы можем создать два набора данных: НаборВсеСобытия и НаборГраницыМаршрутов. В связях наборов данных укажем условие, где НаборВсеСобытия.ВходЗону должно быть между полями начала и конца из второго набора. СКД автоматически выполнит эффективное соединение, что часто работает быстрее и стабильнее, чем "тяжелые" вложенные запросы с группировками.

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

← На главную