При разработке запросов в системе 1С:Предприятие часто возникает задача, когда из нескольких записей, относящихся к одному объекту, нужно выбрать только одну «лучшую» или «приоритетную». Классический пример — выбор скидок для документа. Если у документа есть действующие скидки, мы должны увидеть их все. Однако если действующих скидок нет, документ все равно должен попасть в выборку, но, например, со статусом «Не действует» или пустыми значениями.
В этой статье мы подробно разберем, как реализовать подобную логику выбора записей по условию с использованием различных механизмов платформы 1С.
Проанализируем самый надежный и универсальный способ — присвоение статусам числовых приоритетов. Этот метод отлично работает, когда нужно выбрать одну «самую важную» запись из множества.
Рассмотрим алгоритм реализации по шагам:
МИНИМУМ вычисляем минимальный вес в разрезе каждого документа.Документ и Вес.Посмотрим на пример кода запроса:
// Шаг 1: Присваиваем приоритеты и помещаем во временную таблицу
ВЫБРАТЬ
Скидки.СсылкаДок КАК СсылкаДок,
Скидки.Статус КАК Статус,
ВЫБОР
КОГДА Скидки.Статус = ЗНАЧЕНИЕ(Перечисление.Статусы.Действует) ТОГДА 0
ИНАЧЕ 1
КОНЕЦ КАК Приоритет
ПОМЕСТИТЬ ВТ_ВсеСкидки
ИЗ
РегистрСведений.СкидкиДокументов КАК Скидки;
// Шаг 2: Находим минимальный (лучший) приоритет для каждого документа
ВЫБРАТЬ
ВТ_ВсеСкидки.СсылкаДок КАК СсылкаДок,
МИНИМУМ(ВТ_ВсеСкидки.Приоритет) КАК МинПриоритет
ПОМЕСТИТЬ ВТ_ЛучшиеПриоритеты
ИЗ
ВТ_ВсеСкидки КАК ВТ_ВсеСкидки
СГРУППИРОВАТЬ ПО
ВТ_ВсеСкидки.СсылкаДок;
// Шаг 3: Получаем итоговые записи
ВЫБРАТЬ
ВТ_ВсеСкидки.СсылкаДок,
ВТ_ВсеСкидки.Статус
ИЗ
ВТ_ВсеСкидки КАК ВТ_ВсеСкидки
ВНУТРЕННЕЕ СОЕДИНЕНИЕ ВТ_ЛучшиеПриоритеты КАК ВТ_ЛучшиеПриоритеты
ПО ВТ_ВсеСкидки.СсылкаДок = ВТ_ЛучшиеПриоритеты.СсылкаДок
И ВТ_ВсеСкидки.Приоритет = ВТ_ЛучшиеПриоритеты.МинПриоритет;
Данный подход позволяет гибко управлять логикой: если завтра появится статус «Приостановлено», мы просто добавим еще одно условие ВЫБОР и назначим ему вес (например, 0.5), не меняя общую структуру запроса.
Разберем ситуацию, описанную автором темы: нужно вывести все «Действующие» записи, а если их нет — только одну «Недействующую». Если же есть хотя бы одна «Действующая», «Недействующие» должны быть полностью исключены.
Для этого можно применить левое соединение таблицы самой с собой. Мы ищем для каждой записи со статусом «Не действует» наличие записи со статусом «Действует» по тому же документу. Если такая запись найдена, мы отсекаем «Не действующую» в условии ГДЕ.
Проанализируем пример реализации:
ВЫБРАТЬ
Т1.СсылкаДок,
Т1.Статус,
Т1.Скидка
ИЗ
ВТ_Данные КАК Т1
ЛЕВОЕ СОЕДИНЕНИЕ ВТ_Данные КАК Т2
ПО Т1.СсылкаДок = Т2.СсылкаДок
И Т2.Статус = "Действует"
ГДЕ
(Т1.Статус = "Действует" ИЛИ (Т1.Статус = "Не действует" И Т2.СсылкаДок ЕСТЬ NULL))
Важный момент: если по одному документу может быть несколько «Недействующих» записей и при отсутствии «Действующих» нам нужна только одна любая из них, этот метод потребует дополнительной группировки.
Выясним, как решить задачу через механизм ОБЪЕДИНИТЬ ВСЕ. Этот способ часто оказывается наиболее понятным для чтения и отладки.
Суть заключается в следующем:
Такой подход гарантирует, что если документ попал в первую категорию (активные скидки), он не будет обрабатываться по второй логике (неактивные скидки).
Если вы работаете на платформе 8.3.20 и выше, рассмотрим наиболее современный способ решения задачи. Функция АВТОНОМЕРЗАПИСИ() позволяет пронумеровать строки внутри временной таблицы.
Мы можем упорядочить записи так, чтобы «Действующие» шли первыми, а «Недействующие» последними. Затем, используя нумерацию, мы можем легко отобрать нужные строки.
Посмотрим на логику реализации:
// Помещаем данные во временную таблицу с сортировкой
ВЫБРАТЬ
Скидки.СсылкаДок,
Скидки.Статус
ПОМЕСТИТЬ ВТ_Сортировка
ИЗ
РегистрСведений.СкидкиДокументов КАК Скидки
УПОРЯДОЧИТЬ ПО
Скидки.СсылкаДок,
ВЫБОР КОГДА Скидки.Статус = "Действует" ТОГДА 1 ИНАЧЕ 2 КОНЕЦ;
// Нумеруем записи
ВЫБРАТЬ
ВТ_Сортировка.СсылкаДок,
ВТ_Сортировка.Статус,
АВТОНОМЕРЗАПИСИ() КАК Номер
ПОМЕСТИТЬ ВТ_Нумерация
ИЗ
ВТ_Сортировка;
Затем достаточно найти минимальный номер записи для каждого документа и соединить его с основной таблицей. Этот метод особенно полезен, когда критериев приоритета много (например, дата, сумма и статус одновременно).
Если на выходе нам нужно получить всего одно значение (например, процент скидки), можно использовать элегантный прием с двумя левыми соединениями к одной и той же таблице, но с разными условиями.
Разберем по шагам:
СкидкаДействует.СкидкаНеДействует.ЕСТЬNULL(СкидкаДействует, СкидкаНеДействует).В результате, если первая связь (действующая) не сработает, система автоматически подставит значение из второй связи (недействующей).
При работе с большими объемами данных (например, в исторических регистрах сведений) важно помнить о производительности:
СсылкаДок).СрезПоследних возвращает только одну последнюю запись. Если вам нужны все действующие скидки на момент времени, лучше использовать физическую таблицу регистра с фильтром по периоду.Таким образом, выбор метода зависит от конкретных требований: если нужно получить все действующие записи — используйте весовые коэффициенты или объединение; если нужна строго одна запись — лучше всего подойдет нумерация или агрегатные функции в группировках.