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