Как в запросе 1С выбрать записи с приоритетом по условию

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

При разработке запросов в системе 1С:Предприятие часто возникает задача, когда из нескольких записей, относящихся к одному объекту, нужно выбрать только одну «лучшую» или «приоритетную». Классический пример — выбор скидок для документа. Если у документа есть действующие скидки, мы должны увидеть их все. Однако если действующих скидок нет, документ все равно должен попасть в выборку, но, например, со статусом «Не действует» или пустыми значениями.

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

Метод 1: Использование весовых коэффициентов и группировки

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

Рассмотрим алгоритм реализации по шагам:

  1. Создаем временную таблицу, в которой каждому статусу сопоставляем число. Например, для статуса «Действует» назначим вес 0, а для «Не действует» — вес 1.
  2. С помощью функции МИНИМУМ вычисляем минимальный вес в разрезе каждого документа.
  3. Соединяем исходные данные с полученной таблицей минимальных весов по полям Документ и Вес.

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


// Шаг 1: Присваиваем приоритеты и помещаем во временную таблицу
ВЫБРАТЬ
    Скидки.СсылкаДок КАК СсылкаДок,
    Скидки.Статус КАК Статус,
    ВЫБОР
        КОГДА Скидки.Статус = ЗНАЧЕНИЕ(Перечисление.Статусы.Действует) ТОГДА 0
        ИНАЧЕ 1
    КОНЕЦ КАК Приоритет
ПОМЕСТИТЬ ВТ_ВсеСкидки
ИЗ
    РегистрСведений.СкидкиДокументов КАК Скидки;

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

// Шаг 3: Получаем итоговые записи
ВЫБРАТЬ
    ВТ_ВсеСкидки.СсылкаДок,
    ВТ_ВсеСкидки.Статус
ИЗ
    ВТ_ВсеСкидки КАК ВТ_ВсеСкидки
    ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ЛучшиеПриоритеты КАК ВТ_ЛучшиеПриоритеты
    ПО ВТ_ВсеСкидки.СсылкаДок = ВТ_ЛучшиеПриоритеты.СсылкаДок
    И ВТ_ВсеСкидки.Приоритет = ВТ_ЛучшиеПриоритеты.МинПриоритет;

Данный подход позволяет гибко управлять логикой: если завтра появится статус «Приостановлено», мы просто добавим еще одно условие ВЫБОР и назначим ему вес (например, 0.5), не меняя общую структуру запроса.

Метод 2: Левое соединение с фильтрацией (исключение лишнего)

Разберем ситуацию, описанную автором темы: нужно вывести все «Действующие» записи, а если их нет — только одну «Недействующую». Если же есть хотя бы одна «Действующая», «Недействующие» должны быть полностью исключены.

Для этого можно применить левое соединение таблицы самой с собой. Мы ищем для каждой записи со статусом «Не действует» наличие записи со статусом «Действует» по тому же документу. Если такая запись найдена, мы отсекаем «Не действующую» в условии ГДЕ.

Проанализируем пример реализации:


ВЫБРАТЬ
    Т1.СсылкаДок,
    Т1.Статус,
    Т1.Скидка
ИЗ
    ВТ_Данные КАК Т1
    ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Данные КАК Т2
    ПО Т1.СсылкаДок = Т2.СсылкаДок
    И Т2.Статус = "Действует"
ГДЕ
    (Т1.Статус = "Действует" ИЛИ (Т1.Статус = "Не действует" И Т2.СсылкаДок ЕСТЬ NULL))

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

Метод 3: Разделение на две временные таблицы (Объединение)

Выясним, как решить задачу через механизм ОБЪЕДИНИТЬ ВСЕ. Этот способ часто оказывается наиболее понятным для чтения и отладки.

Суть заключается в следующем:

  1. В первой части запроса мы выбираем только те документы, у которых есть статус «Действует».
  2. Во второй части мы выбираем документы, у которых нет статуса «Действует», и соединяем их со статусом «Не действует».
  3. Объединяем результаты.

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

Метод 4: Современный подход с функцией АВТОНОМЕРЗАПИСИ()

Если вы работаете на платформе 8.3.20 и выше, рассмотрим наиболее современный способ решения задачи. Функция АВТОНОМЕРЗАПИСИ() позволяет пронумеровать строки внутри временной таблицы.

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

Посмотрим на логику реализации:


// Помещаем данные во временную таблицу с сортировкой
ВЫБРАТЬ
    Скидки.СсылкаДок,
    Скидки.Статус
ПОМЕСТИТЬ ВТ_Сортировка
ИЗ
    РегистрСведений.СкидкиДокументов КАК Скидки
УПОРЯДОЧИТЬ ПО
    Скидки.СсылкаДок,
    ВЫБОР КОГДА Скидки.Статус = "Действует" ТОГДА 1 ИНАЧЕ 2 КОНЕЦ;

// Нумеруем записи
ВЫБРАТЬ
    ВТ_Сортировка.СсылкаДок,
    ВТ_Сортировка.Статус,
    АВТОНОМЕРЗАПИСИ() КАК Номер
ПОМЕСТИТЬ ВТ_Нумерация
ИЗ
    ВТ_Сортировка;

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

Метод 5: Использование двух полей и функции ЕСТЬNULL

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

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

  1. Присоединяем таблицу скидок по условию «Статус = Действует». Называем поле СкидкаДействует.
  2. Присоединяем ту же таблицу по условию «Статус = Не действует». Называем поле СкидкаНеДействует.
  3. В поле выбора пишем конструкцию: ЕСТЬNULL(СкидкаДействует, СкидкаНеДействует).

В результате, если первая связь (действующая) не сработает, система автоматически подставит значение из второй связи (недействующей).

Рекомендации по оптимизации

При работе с большими объемами данных (например, в исторических регистрах сведений) важно помнить о производительности:

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

← На главную