При работе с регистрами накопления в системе «1С:Предприятие 8» разработчики часто сталкиваются с необходимостью отфильтровать движения не просто по типу документа, но и по значению конкретного реквизита внутри этого документа. Типичный пример: из регистра ТоварыНаСкладах нужно исключить документы ПриходныйОрдерНаТовары, у которых вид операции установлен как «Приемка по перемещению», но при этом оставить все остальные типы документов и другие операции этого же ордера.
Основная сложность заключается в том, что поле Регистратор имеет составной тип данных. Прямая попытка наложить фильтр «через точку» (например, Регистратор.ВидОперации <> &Значение) приводит к тому, что из результирующей таблицы исчезают вообще все документы, у которых нет реквизита ВидОперации. В этой статье мы подробно разберем, почему это происходит и как правильно составить запрос, чтобы получить корректный результат.
Разберем ситуацию с точки зрения работы СУБД. Когда мы обращаемся к полю Регистратор.ВидОперации, платформа 1С на уровне SQL-запроса выполняет неявное левое соединение (LEFT JOIN) со всеми таблицами документов, которые могут выступать регистраторами для данного регистра. Если текущая запись регистра порождена документом РеализацияТоваровУслуг, у которого нет поля ВидОперации, результатом обращения будет NULL.
Согласно логике трехзначной логики SQL, любое сравнение с NULL (равенство, неравенство, больше или меньше) дает результат «Ложь» (Unknown). Таким образом, условие ГДЕ Регистратор.ВидОперации <> &Операция отсекает не только нужную нам операцию, но и все документы других типов, так как для них результат сравнения NULL <> &Операция не является истинным. Проанализируем способы решения этой задачи.
Самый простой и надежный способ — это четко ограничить область действия фильтра конкретным типом документа. Мы должны сказать системе: «Исключи записи только в том случае, если это Приходный ордер И у него конкретная операция. Во всех остальных случаях записи должны остаться».
Рассмотрим пример правильного условия в запросе:
ГДЕ НЕ (Регистратор ССЫЛКА Документ.ПриходныйОрдерНаТовары
И Регистратор.ВидОперации = &ИсключаемаяОперация)
В данном случае логика работает следующим образом:
Реализацией, условие в скобках дает «Ложь», инверсия НЕ превращает её в «Истину» — документ остается.Приходным ордером с другой операцией, условие в скобках снова «Ложь», инверсия дает «Истину» — документ остается.НЕ превращает её в «Ложь» — документ исключается.Иногда удобнее использовать явное ветвление в блоке условий. Это особенно полезно, если логика отбора сложная и включает несколько видов документов. Посмотрим на реализацию этого подхода:
ГДЕ ВЫБОР
КОГДА Регистратор ССЫЛКА Документ.ПриходныйОрдерНаТовары
ТОГДА Регистратор.ВидОперации <> &ИсключаемаяОперация
ИНАЧЕ ИСТИНА
КОНЕЦ
Этот метод позволяет наглядно управлять поведением фильтра для разных типов регистраторов. Мы явно указываем, что для всех документов, кроме Приходного ордера, условие всегда возвращает ИСТИНА.
При обращении к реквизитам через составное поле Регистратор, система может генерировать избыточные соединения с таблицами БД. Чтобы оптимизировать запрос и помочь планировщику SQL, крайне рекомендуется использовать функцию ВЫРАЗИТЬ. Это явно указывает платформе, к таблице какого именно документа нужно обратиться.
Проанализируем оптимизированный вариант запроса:
ГДЕ ВЫБОР
КОГДА Регистратор ССЫЛКА Документ.ПриходныйОрдерНаТовары
ТОГДА ВЫРАЗИТЬ(Регистратор КАК Документ.ПриходныйОрдерНаТовары).ВидОперации <> &ИсключаемаяОперация
ИНАЧЕ ИСТИНА
КОНЕЦ
Использование ВЫРАЗИТЬ исключает попытки поиска реквизита ВидОперации в других таблицах, что положительно сказывается на скорости выполнения запроса, особенно в крупных базах данных с большим количеством типов документов-регистраторов.
Важно помнить, в какой именно таблице регистра мы пытаемся применить отбор. Выясним разницу между ними:
Регистратору здесь технически невозможна на уровне параметров виртуальной таблицы, так как остатки — это срез данных на момент времени, они не «помнят», каким документом созданы. Если нужно получить остатки за вычетом конкретных движений, придется вычитать обороты этих документов из общих остатков вручную в запросе.ВЫРАЗИТЬ и ССЫЛКА обязательны к применению для обеспечения производительности.Если задача исключения определенных операций из отчетов возникает регулярно и отчет строится по большим массивам данных, использование соединений с документами может сильно замедлить систему. Рассмотрим альтернативные пути:
1. Добавление измерения в регистр. Если бизнес-логика требует частого разделения движений по видам операций (например, «Закупка», «Перемещение», «Продажа»), имеет смысл вынести этот реквизит непосредственно в Измерения регистра накопления. В этом случае фильтрация в виртуальных таблицах будет происходить мгновенно, так как данные будут проиндексированы СУБД по этому полю.
2. Использование СКД. При настройке отчетов в Системе компоновки данных (СКД), старайтесь выносить подобные условия в параметры виртуальных таблиц. СКД умеет интеллектуально преобразовывать условия, но использование конструкции ВЫБОР КОГДА ... ССЫЛКА ... внутри параметров все равно остается лучшей практикой.
3. Учет RLS. Помните, что обращение «через точку» к регистратору активирует механизмы ограничения прав доступа (RLS) для всех типов документов, входящих в составной тип. Если у пользователя нет прав на какой-то тип документа, запрос может завершиться ошибкой или работать крайне медленно. Явное приведение типа через ВЫРАЗИТЬ помогает минимизировать это влияние.
Таким образом, для решения задачи отбора по составному регистратору мы должны комбинировать проверку типа документа и корректную обработку значений NULL, не забывая о производительности запроса.