Как в запросе 1С получить минимальный и максимальный пробег автомобиля или контрагента

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

При работе с базами данных автосервисов, транспортных компаний или автопарков часто возникает задача анализа эксплуатации техники. Одна из типовых задач — определить рамки активности автомобиля: его минимальный и максимальный пробег по имеющимся документам (например, по документам типа Заказ-наряд). Это позволяет быстро оценить общую дистанцию, которую проехал автомобиль за время обслуживания, или выявить некорректно заполненные данные.

В этой статье мы подробно разберем, как решить эту задачу исключительно средствами языка запросов 1С. Мы рассмотрим процесс создания запроса с нуля через Конструктор, научимся правильно обрабатывать нулевые значения и разберем оптимальные архитектурные практики для обеспечения высокой скорости работы базы данных.

Шаг 1. Создание базового запроса через Конструктор запросов

Основная сложность, с которой часто сталкиваются начинающие разработчики при работе в Конструкторе запросов — это необходимость выбрать одно и то же поле (в нашем случае — Пробег) дважды, чтобы применить к нему разные агрегатные функции (МИНИМУМ и МАКСИМУМ).

Рассмотрим по шагам, как правильно настроить такой запрос в Конструкторе:

  1. Откроем Конструктор запросов и перейдем на вкладку «Таблицы и поля».
  2. В качестве источника данных выберем таблицу документов, например, Документ.ЗаказНаряд.
  3. Перетащим поле Контрагент (или Автомобиль) в список выбранных полей.
  4. Перетащим поле Пробег в список выбранных полей первый раз. Переименуем его на вкладке «Объединения/Псевдонимы» в ПробегМинимум.
  5. Перетащим поле Пробег в список выбранных полей второй раз. Переименуем его в ПробегМаксимум. Конструктор запросов позволяет выбирать одно и то же поле неограниченное количество раз, присваивая им уникальные псевдонимы.
  6. Перейдем на вкладку «Группировка».
  7. Поле Контрагент перенесем в левое окно («Группировочные поля»).
  8. Поля ПробегМинимум и ПробегМаксимум перенесем в правое окно («Суммируемые поля»).
  9. В колонке «Функция» для поля ПробегМинимум выберем значение Минимум, а для поля ПробегМаксимум — значение Максимум.

В результате работы Конструктора мы получим следующий лаконичный текст запроса:


ВЫБРАТЬ
    ЗаказНаряд.Контрагент КАК Контрагент,
    МИНИМУМ(ЗаказНаряд.Пробег) КАК ПробегМинимум,
    МАКСИМУМ(ЗаказНаряд.Пробег) КАК ПробегМаксимум
ИЗ
    Документ.ЗаказНаряд КАК ЗаказНаряд
ГДЕ
    НЕ ЗаказНаряд.ПометкаУдаления
СГРУППИРОВАТЬ ПО
    ЗаказНаряд.Контрагент

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

Шаг 2. Исключение нулевых значений пробега

В реальных базах данных пользователи часто забывают заполнить поле пробега в Заказ-наряде, оставляя его пустым, либо система по умолчанию записывает туда значение 0. Если мы применим базовый запрос к такой базе, то в колонке ПробегМинимум для большинства клиентов мы получим значение 0, что полностью лишает отчет практического смысла.

Проанализируем, как можно исключить нули из расчета минимального пробега.

Неправильный подход: использование «магических чисел»

Иногда разработчики пытаются заменить нули на очень большое число (например, 999999) прямо внутри функции минимума:


МИНИМУМ(ВЫБОР КОГДА ЗаказНаряд.Пробег = 0 ТОГДА 999999 ИНАЧЕ ЗаказНаряд.Пробег КОНЕЦ)

Почему так делать нельзя? Если у контрагента будет всего один документ Заказ-наряд, и пробег в нем равен 0, то функция МИНИМУМ вернет 999999. Более того, если мы захотим применить аналогичную логику ко всей таблице, это число может исказить другие расчеты или случайно попасть в максимальные значения, запутав пользователей.

Правильный подход: использование особенностей значения NULL

Стандарты SQL и внутренний язык запросов 1С спроектированы так, что любые агрегатные функции (включая МИНИМУМ и МАКСИМУМ) полностью **игнорируют** значения типа NULL. Мы можем использовать это свойство в своих целях.

Если мы преобразуем нулевые значения пробега в NULL, функция МИНИМУМ просто не будет брать их в расчет при группировке. Для этого применим конструкцию ВЫБОР КОГДА ... ТОГДА:


ВЫБРАТЬ
    ЗаказНаряд.Контрагент КАК Контрагент,
    МИНИМУМ(ВЫБОР
            КОГДА ЗаказНаряд.Пробег = 0
                ТОГДА NULL
            ИНАЧЕ ЗаказНаряд.Пробег
        КОНЕЦ) КАК ПробегМинимум,
    МАКСИМУМ(ЗаказНаряд.Пробег) КАК ПробегМаксимум
ИЗ
    Документ.ЗаказНаряд КАК ЗаказНаряд
ГДЕ
    НЕ ЗаказНаряд.ПометкаУдаления
СГРУППИРОВАТЬ ПО
    ЗаказНаряд.Контрагент

В этом случае, если у контрагента есть три документа с пробегами 0, 5000 и 12000, система превратит 0 в NULL, проигнорирует его и вернет корректный минимум: 5000.

Шаг 3. Фильтрация по табличной части документа

Бывают ситуации, когда пробег нужно анализировать не по всем Заказ-нарядам подряд, а только по тем, в которых выполнялись определенные работы (например, проводилось регламентное ТО или замена масла). В этом случае нам необходимо наложить условие на табличную часть документа.

Для того чтобы не перегружать основной запрос соединениями с табличными частями (что может привести к дублированию строк и замедлению вычислений), рекомендуется использовать механизм связи по условию В (ВЫБРАТЬ...). Рассмотрим пример такого запроса:


ВЫБРАТЬ
    ЗаказНаряд.Контрагент КАК Контрагент,
    МИНИМУМ(ВЫБОР
            КОГДА ЗаказНаряд.Пробег = 0
                ТОГДА NULL
            ИНАЧЕ ЗаказНаряд.Пробег
        КОНЕЦ) КАК ПробегМинимум,
    МАКСИМУМ(ЗаказНаряд.Пробег) КАК ПробегМаксимум
ИЗ
    Документ.ЗаказНаряд КАК ЗаказНаряд
ГДЕ
    НЕ ЗаказНаряд.ПометкаУдаления
    И ЗаказНаряд.Ссылка В
        (ВЫБРАТЬ РАЗЛИЧНЫЕ
            ТабличнаяЧастьРаботы.Ссылка
        ИЗ
            Документ.ЗаказНаряд.Работы КАК ТабличнаяЧастьРаботы
        ГДЕ
            ТабличнаяЧастьРаботы.Работа = &ЦелеваяРабота)
СГРУППИРОВАТЬ ПО
    ЗаказНаряд.Контрагент

Использование конструкции ВЫБРАТЬ РАЗЛИЧНЫЕ во вложенном запросе гарантирует, что мы получим только уникальный список ссылок на документы, избегая избыточного дублирования записей при объединении таблиц.

Шаг 4. Минимальный и максимальный пробег vs Первый и последний визит

Важно различать математические экстремумы (минимальный и максимальный пробег за всю историю) и хронологические показатели (пробег при первом визите клиента и пробег при последнем визите).

В реальной жизни пробег автомобиля должен расти линейно. Однако из-за ошибок ввода или скрутки одометра минимальный пробег в базе данных не всегда соответствует самому первому визиту. Если перед нами стоит задача получить именно хронологические данные (пробег на дату первого и последнего заказ-наряда), обычные функции МИНИМУМ и МАКСИМУМ для поля Пробег не подойдут.

Для решения этой задачи нам потребуется применить двухэтапный запрос с использованием временных таблиц:

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

Проанализируем этот алгоритм на примере следующего запроса:


ВЫБРАТЬ
    ЗаказНаряд.Контрагент КАК Контрагент,
    МИНИМУМ(ЗаказНаряд.Дата) КАК ДатаПервая,
    МАКСИМУМ(ЗаказНаряд.Дата) КАК ДатаПоследняя
ПОМЕСТИТЬ ВТ_ДатыВизитов
ИЗ
    Документ.ЗаказНаряд КАК ЗаказНаряд
ГДЕ
    НЕ ЗаказНаряд.ПометкаУдаления
    И ЗаказНаряд.Пробег > 0
СГРУППИРОВАТЬ ПО
    ЗаказНаряд.Контрагент
;

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

Обратите внимание: Мы используем агрегатную функцию МАКС при получении полей ПробегНачальный и ПробегКонечный. Это необходимо для того, чтобы избежать дублирования строк, если в одну и ту же секунду (на одну дату) по какому-то контрагенту было проведено несколько документов.

Шаг 5. Оптимизация производительности запроса

Запросы напрямую к таблицам документов — это ресурсоемкая операция для СУБД. В отличие от регистров накопления или сведений, документы не имеют предварительно рассчитанных итогов. Если в вашей базе сотни тысяч заказ-нарядов, выполнение такого отчета может занимать значительное время.

Чтобы ускорить выполнение запроса, придерживайтесь следующих правил:

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

← На главную