При работе с системой 1С:ERP 2.x часто возникает необходимость получить информацию из связанных документов. В данном случае мы рассмотрим ситуацию, когда требуется извлечь список заказов клиента, которые являются основаниями для документа "Реализация товаров и услуг", а также получить дополнительные реквизиты, связанные с этими заказами. Изначально может показаться, что для хранения нескольких заказов используется реквизит типа СписокЗначений, однако это не так. Разберем подробнее, как корректно построить запросы для решения этой задачи.
Прежде всего, давайте выясним причину распространенного заблуждения относительно реквизитов типа СписокЗначений. В 1С СписокЗначений — это коллекция, используемая преимущественно в коде или для интерфейсных элементов, но не для непосредственного хранения данных в информационной базе. Если документ (например, "Реализация товаров и услуг") связан с несколькими другими документами (например, "Заказами клиента"), эта связь обычно реализуется через табличные части основного документа или через регистры сведений.
В типовой конфигурации 1С:ERP 2.x документ "Реализация товаров и услуг" может быть оформлен на основании нескольких "Заказов клиента". Эта связь формируется через табличную часть «Товары» документа "Реализация товаров и услуг". Каждая строка табличной части "Товары" содержит поле ЗаказКлиента, которое ссылается на соответствующий заказ.
Дополнительные реквизиты, такие как "Иные сведения из заказа", также не хранятся напрямую в основной таблице документа. Для их хранения используются специальные табличные части или регистры сведений, которые связываются с основным объектом (в нашем случае, с документом "Заказ клиента"). Это означает, что для получения значений дополнительных реквизитов в запросе всегда потребуется дополнительное соединение с соответствующей таблицей (например, Документ.ЗаказКлиента.ДополнительныеРеквизиты).
Начнем с основной задачи: получить список всех уникальных заказов клиента, связанных с конкретной реализацией. Поскольку связь устанавливается через табличную часть "Товары", нам необходимо обратиться именно к ней. Мы будем использовать оператор ВЫБРАТЬ РАЗЛИЧНЫЕ, чтобы избежать дублирования заказов, если в реализации есть несколько строк товаров, ссылающихся на один и тот же заказ.
Рассмотрим пример запроса:
ВЫБРАТЬ РАЗЛИЧНЫЕ
Док.ЗаказКлиента КАК ЗаказКлиента
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК Док
ГДЕ
Док.Ссылка = &Ссылка
И Док.ЗаказКлиента.Ссылка ЕСТЬ НЕ NULL
В этом запросе:
ЗаказКлиента из табличной части Документ.РеализацияТоваровУслуг.Товары.КАК Док для краткости обращения.Док.Ссылка = &Ссылка позволяет отфильтровать данные по конкретному документу реализации, передаваемому как параметр.Док.ЗаказКлиента.Ссылка ЕСТЬ НЕ NULL гарантирует, что мы получим только те строки, где действительно указан заказ клиента, избегая пустых ссылок.ВЫБРАТЬ РАЗЛИЧНЫЕ исключает дубликаты, если один и тот же заказ встречается в нескольких строках табличной части "Товары" данной реализации.Теперь, когда мы знаем, как получить список заказов, перейдем к более сложной части: получению дополнительных реквизитов из этих заказов. Проблема часто возникает, когда нужно получить значение дополнительного реквизита из заказа, даже если в самой реализации нет соответствующих дополнительных реквизитов или если дополнительный реквизит не заполнен в некоторых заказах. Здесь на помощь приходят левые соединения и правильное размещение условий.
Это критически важный момент для построения корректных запросов в 1С. Разберем принципы:
NULL. Это позволяет нам сохранить все данные из основной таблицы, даже если связанные данные отсутствуют.ПО (ON): Условия, указанные в секции ПО для ЛЕВОГО СОЕДИНЕНИЯ, применяются до объединения таблиц. Это означает, что они фильтруют строки правой таблицы *перед* тем, как произойдет соединение. Строки из левой таблицы, для которых не нашлось совпадений в правой таблице (с учетом условий в ПО), все равно будут включены в результат, а поля правой таблицы будут NULL.ГДЕ (WHERE): Условия, указанные в секции ГДЕ, применяют фильтрацию после того, как соединение уже выполнено. Если в условии ГДЕ используется поле из правой таблицы ЛЕВОГО СОЕДИНЕНИЯ и это поле не равно NULL (т.е. мы ищем конкретное значение), то строки, для которых в правой таблице не нашлось соответствий (и чьи поля были заполнены NULL), будут отсечены. Это фактически превращает ЛЕВОЕ СОЕДИНЕНИЕ во ВНУТРЕННЕЕ СОЕДИНЕНИЕ (INNER JOIN) для этих строк, поскольку отбрасываются все строки, для которых в правой таблице не было соответствия или условие в ГДЕ не выполнилось для NULL-значений.Для нашей задачи — получить дополнительный реквизит "Иные сведения из заказа" из документа "Заказ клиента" — нам необходимо использовать ЛЕВОЕ СОЕДИНЕНИЕ. Условие по имени дополнительного реквизита (например, ЗаказКлиентаДополнительныеРеквизиты.Свойство = &ДопРеквизитИныеСведенияИзЗаказа) следует перенести из секции ГДЕ в секцию ПО. Это гарантирует, что мы увидим все реализации и их заказы, даже если у заказа нет нужного дополнительного реквизита или он не заполнен.
Давайте проанализируем запрос, который изначально приводил к проблемам:
ВЫБРАТЬ
РеализацияТоваровУслугТовары.Ссылка КАК Реализация,
РеализацияТоваровУслугТовары.ЗаказКлиента КАК Заказ,
ЗаказКлиентаДополнительныеРеквизиты.Значение КАК ЗначениеДопРеквизитаЗаказ
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары
ЛЕВОЕ СОЕДИНЕНИЕ Документ.ЗаказКлиента.ДополнительныеРеквизиты КАК ЗаказКлиентаДополнительныеРеквизиты
ПО (РеализацияТоваровУслугТовары.ЗаказКлиента = ЗаказКлиентаДополнительныеРеквизиты.Ссылка)
ГДЕ
РеализацияТоваровУслугТовары.Ссылка = &Ссылка
И ЗаказКлиентаДополнительныеРеквизиты.Свойство = &ДопРеквизитИныеСведенияИзЗаказа
В этом запросе проблема заключается в строке И ЗаказКлиентаДополнительныеРеквизиты.Свойство = &ДопРеквизитИныеСведенияИзЗаказа, находящейся в секции ГДЕ. Если для какого-либо ЗаказКлиента не существует дополнительного реквизита с заданным свойством (или его значение равно NULL), то соответствующая строка из Документ.ЗаказКлиента.ДополнительныеРеквизиты не будет найдена, и поля из этой таблицы станут NULL. Условие в ГДЕ, применяемое к NULL-значению, отсечет эту строку, фактически превратив ЛЕВОЕ СОЕДИНЕНИЕ во ВНУТРЕННЕЕ СОЕДИНЕНИЕ.
Чтобы избежать этого и получить все реализации с заказами, даже если доп. реквизит не найден, нам необходимо перенести условие на дополнительный реквизит в секцию ПО:
ВЫБРАТЬ
РеализацияТоваровУслугТовары.Ссылка КАК Реализация,
РеализацияТоваровУслугТовары.ЗаказКлиента КАК Заказ,
РеализацияТоваровУслугТовары.ЗаказКлиента.Контрагент КАК ЗаказКлиентаКонтрагент,
ЗаказКлиентаДополнительныеРеквизиты.Значение КАК Значение
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары
ЛЕВОЕ СОЕДИНЕНИЕ Документ.ЗаказКлиента.ДополнительныеРеквизиты КАК ЗаказКлиентаДополнительныеРеквизиты
ПО (РеализацияТоваровУслугТовары.ЗаказКлиента = ЗаказКлиентаДополнительныеРеквизиты.Ссылка)
И (ЗаказКлиентаДополнительныеРеквизиты.Свойство = &ДопРеквизитИныеСведенияИзЗаказа)
ГДЕ
РеализацияТоваровУслугТовары.Ссылка = &Ссылка
Разберем этот исправленный запрос:
Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары. Это логично, поскольку именно эта табличная часть содержит ссылки на заказы клиента.ЛЕВОЕ СОЕДИНЕНИЕ с таблицей Документ.ЗаказКлиента.ДополнительныеРеквизиты КАК ЗаказКлиентаДополнительныеРеквизиты.ПО мы указываем два условия:
РеализацияТоваровУслугТовары.ЗаказКлиента = ЗаказКлиентаДополнительныеРеквизиты.Ссылка: это основное условие соединения по ссылке на заказ.ЗаказКлиентаДополнительныеРеквизиты.Свойство = &ДопРеквизитИныеСведенияИзЗаказа: это условие фильтрации по конкретному дополнительному реквизиту. Перенос его в ПО означает, что мы пытаемся найти соответствующий доп. реквизит *во время* соединения. Если он не находится, поля из ЗаказКлиентаДополнительныеРеквизиты будут NULL, но строка из РеализацияТоваровУслугТовары все равно останется в результате.ГДЕ мы оставляем только условие РеализацияТоваровУслугТовары.Ссылка = &Ссылка, чтобы отфильтровать данные по конкретной реализации. Это условие применяется к базовой таблице, поэтому оно безопасно.Данный запрос обеспечит нам список всех заказов, связанных с указанной реализацией, и для каждого заказа будет показано значение дополнительного реквизита "Иные сведения из заказа". Если у заказа такой реквизит отсутствует или не заполнен, соответствующее поле Значение будет NULL, что является ожидаемым поведением для ЛЕВОГО СОЕДИНЕНИЯ.
Если нам требуется получить не только значение дополнительного реквизита, но и, например, контрагента заказа клиента, то его можно получить прямым обращением через точку к ссылке на заказ:
ВЫБРАТЬ
РеализацияТоваровУслугТовары.Ссылка КАК Реализация,
РеализацияТоваровУслугТовары.ЗаказКлиента КАК Заказ,
РеализацияТоваровУслугТовары.ЗаказКлиента.Контрагент КАК ЗаказКлиентаКонтрагент,
ЗаказКлиентаДополнительныеРеквизиты.Значение КАК ЗначениеДопРеквизитаЗаказ
ИЗ
Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары
ЛЕВОЕ СОЕДИНЕНИЕ Документ.ЗаказКлиента.ДополнительныеРеквизиты КАК ЗаказКлиентаДополнительныеРеквизиты
ПО (РеализацияТоваровУслугТовары.ЗаказКлиента = ЗаказКлиентаДополнительныеРеквизиты.Ссылка)
И (ЗаказКлиентаДополнительныеРеквизиты.Свойство = &ДопРеквизитИныеСведенияИзЗаказа)
ГДЕ
РеализацияТоваровУслугТовары.Ссылка = &Ссылка
В этом запросе мы добавили РеализацияТоваровУслугТовары.ЗаказКлиента.Контрагент. Это возможно, так как ЗаказКлиента является ссылочным типом данных, и через него можно напрямую обращаться к реквизитам связанного документа.
Для выгрузки в ДиаДок, как в нашем случае, мы можем получить нужные нам сведения:
Оптимизация запросов:
ВЫБРАТЬ РАЗЛИЧНЫЕ в самом начале запроса, чтобы исключить повторяющиеся комбинации значений. Однако будьте внимательны, РАЗЛИЧНЫЕ может быть ресурсоемкой операцией на больших объемах данных.&Ссылка, &ДопРеквизитыИныеСведенияИзЗаказа) передаются корректно и имеют правильный тип данных.Изучив эти принципы, вы сможете более эффективно работать со сложными запросами в 1С:ERP, корректно извлекая данные из связанных объектов и их дополнительных реквизитов.