Как вывести номенклатуру (товары и услуги) из табличной части документа, показывая дополнительные реквизиты только для товаров?

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

При работе с запросами в 1С часто возникает задача, когда необходимо получить данные из табличной части документа, которая содержит разнородные элементы, например, товары и услуги. При этом для одной категории элементов (товаров) требуется вывести дополнительные реквизиты, а для другой (услуг) — нет, сохраняя при этом все записи обеих категорий. В этой статье мы подробно рассмотрим, как правильно построить такой запрос, используя ЛЕВОЕ СОЕДИНЕНИЕ и понимая разницу между условиями в секциях ПО и ГДЕ.

Давайте проанализируем исходную проблему, которую часто допускают при составлении таких запросов.

Исходная проблема и некорректный запрос

Представим, что у нас есть документ "Реализация товаров и услуг", который содержит табличную часть с номенклатурой. В этой табличной части могут быть как товары, так и услуги. Для товаров требуется вывести значения дополнительных реквизитов, таких как "Класс" и "ОсновнаяГруппа". При попытке решить эту задачу, программисты часто сталкиваются с одной из двух проблем:

  1. Выводятся только товары со значениями дополнительных реквизитов, а услуги полностью исключаются из результата.
  2. Выводятся и товары, и услуги, но со всеми значениями дополнительных реквизитов, даже тех, которые не нужны или не относятся к услугам.

Вот пример запроса, с которым можно столкнуться при первой попытке решения:


ВЫБРАТЬ
    РеализацияТоваровУслугТовары.Ссылка КАК Ссылка,
    РеализацияТоваровУслугТовары.Номенклатура КАК Номенклатура,
    НоменклатураДополнительныеРеквизиты.Значение КАК Значение
ИЗ
    Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары
        ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура.ДополнительныеРеквизиты КАК НоменклатураДополнительныеРеквизиты
        ПО РеализацияТоваровУслугТовары.Номенклатура = НоменклатураДополнительныеРеквизиты.Ссылка
ГДЕ
    РеализацияТоваровУслугТовары.Ссылка = &Ссылка
    И НоменклатураДополнительныеРеквизиты.Свойство.Имя = "ОсновнаяГруппа_2e29e30df745476fa8e61ecaea95e230"

В этом запросе мы используем ЛЕВОЕ СОЕДИНЕНИЕ, чтобы попытаться присоединить дополнительные реквизиты. Однако, ключевая ошибка заключается в размещении условия на дополнительный реквизит (НоменклатураДополнительныеРеквизиты.Свойство.Имя = "...") в секции ГДЕ.

Различие между условиями в секциях ПО и ГДЕ при ЛЕВОМ СОЕДИНЕНИИ

Для того чтобы понять, почему предыдущий запрос работает некорректно и как его исправить, нам необходимо четко осознать разницу в поведении условий, наложенных на правую таблицу при ЛЕВОМ СОЕДИНЕНИИ, в зависимости от того, находятся ли они в секции ПО или ГДЕ.

  1. Условие в секции ПО (ON)

    Когда мы накладываем условие на правую таблицу в секции ПО при использовании ЛЕВОГО СОЕДИНЕНИЯ, это условие применяется до выполнения самого соединения. Оно фильтрует записи из правой таблицы (в нашем случае, Справочник.Номенклатура.ДополнительныеРеквизиты) еще до того, как они будут соединены с записями из левой таблицы (Документ.РеализацияТоваровУслуг.Товары).

    • Если для записи из левой таблицы (например, услуги) не находится соответствующая запись в правой таблице, которая бы удовлетворяла условию в ПО, то поля правой таблицы в результирующей выборке будут содержать значение NULL.
    • Важный момент: Запись из левой таблицы (например, услуга) при этом все равно будет выведена в результат запроса.

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

  2. Условие в секции ГДЕ (WHERE)

    Если мы накладываем условие на правую таблицу в секции ГДЕ при использовании ЛЕВОГО СОЕДИНЕНИЯ, то это условие применяется после того, как соединение уже было выполнено и сформирована объединенная таблица.

    • В этом случае, если для записи из левой таблицы (например, услуги) в правой таблице не нашлось соответствия (т.е. поля правой таблицы получили NULL), или если найденное соответствие не удовлетворяет условию в ГДЕ, то вся строка (включая запись из левой таблицы) будет исключена из результата запроса.
    • Результат: Таким образом, ЛЕВОЕ СОЕДИНЕНИЕ с условием на правую таблицу в ГДЕ фактически начинает работать как ВНУТРЕННЕЕ СОЕДИНЕНИЕ, отфильтровывая записи из левой таблицы, для которых нет совпадений, удовлетворяющих условию. Именно поэтому в нашем исходном запросе услуги не выводились, так как у них не было дополнительных реквизитов, удовлетворяющих условию в ГДЕ, и вся строка удалялась.

Корректное решение: перемещаем условие в секцию ПО

Основываясь на понимании различий, правильное решение заключается в том, чтобы перенести условие на тип дополнительного реквизита из секции ГДЕ в секцию ПО оператора ЛЕВОЕ СОЕДИНЕНИЕ. Это позволит нам вывести всю номенклатуру (как товары, так и услуги) из табличной части, но дополнительные реквизиты "Класс" и "ОсновнаяГруппа" будут присоединяться только для тех записей, которые имеют соответствующие реквизиты, удовлетворяющие условию.

Посмотрим на исправленный запрос:


ВЫБРАТЬ
    РеализацияТоваровУслугТовары.Ссылка КАК Ссылка,
    РеализацияТоваровУслугТовары.Номенклатура КАК Номенклатура,
    НоменклатураДополнительныеРеквизиты.Значение КАК Значение
ИЗ
    Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары
        ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура.ДополнительныеРеквизиты КАК НоменклатураДополнительныеРеквизиты
        ПО РеализацияТоваровУслугТовары.Номенклатура = НоменклатураДополнительныеРеквизиты.Ссылка
        И НоменклатураДополнительныеРеквизиты.Свойство.Имя = "ОсновнаяГруппа_2e29e30df745476fa8e61ecaea95e230"
ГДЕ
    РеализацияТоваровУслугТовары.Ссылка = &Ссылка

В этом запросе:

  1. РеализацияТоваровУслугТовары — это наша левая таблица, из которой мы хотим получить все записи.
  2. Справочник.Номенклатура.ДополнительныеРеквизиты — это правая таблица, из которой мы хотим получить значения только для определенных типов реквизитов.
  3. Условие РеализацияТоваровУслугТовары.Номенклатура = НоменклатураДополнительныеРеквизиты.Ссылка связывает номенклатуру с её дополнительными реквизитами.
  4. Ключевое изменение: И НоменклатураДополнительныеРеквизиты.Свойство.Имя = "ОсновнаяГруппа_2e29e30df745476fa8e61ecaea95e230" теперь находится в секции ПО. Это означает, что дополнительные реквизиты будут присоединяться к номенклатуре только в том случае, если они соответствуют указанному имени свойства.
  5. Если для какой-либо номенклатуры (например, для услуги) не найдется дополнительного реквизита с именем "ОсновнаяГруппа_2e29e30df745476fa8e61ecaea95e230", то поля Значение (и любые другие поля из НоменклатураДополнительныеРеквизиты) для этой строки будут содержать NULL, но сама строка с услугой не будет отфильтрована.
  6. Секция ГДЕ содержит только условие на ссылку документа, чтобы получить записи только из одного конкретного документа.

Таким образом, мы добиваемся желаемого результата: выводятся все записи номенклатуры из документа, а значения дополнительных реквизитов показываются только для тех позиций (товаров), для которых они существуют и соответствуют заданному условию. Для услуг в поле Значение будет NULL.

Обработка NULL значений

Для более удобного отображения результатов, когда для услуг в полях дополнительных реквизитов выводится NULL, мы можем использовать функцию ЕСТЬNULL (аналог ISNULL в других SQL-диалектах 1С). Эта функция позволяет заменить NULL на другое, более подходящее значение, например, на пустую строку.


ВЫБРАТЬ
    РеализацияТоваровУслугТовары.Ссылка КАК Ссылка,
    РеализацияТоваровУслугТовары.Номенклатура КАК Номенклатура,
    ЕСТЬNULL(НоменклатураДополнительныеРеквизиты.Значение, "") КАК ЗначениеДополнительногоРеквизита
ИЗ
    Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары
        ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура.ДополнительныеРеквизиты КАК НоменклатураДополнительныеРеквизиты
        ПО РеализацияТоваровУслугТовары.Номенклатура = НоменклатураДополнительныеРеквизиты.Ссылка
        И НоменклатураДополнительныеРеквизиты.Свойство.Имя = "ОсновнаяГруппа_2e29e30df745476fa8e61ecaea95e230"
ГДЕ
    РеализацияТоваровУслугТовары.Ссылка = &Ссылка

В этом примере, если НоменклатураДополнительныеРеквизиты.Значение окажется NULL (например, для услуги), оно будет заменено на пустую строку.

Выборка нескольких дополнительных реквизитов

Если нам необходимо вывести несколько различных дополнительных реквизитов (например, "Класс" и "ОсновнаяГруппа"), мы не можем просто добавить несколько условий на НоменклатураДополнительныеРеквизиты.Свойство.Имя в одной секции ПО, так как одно поле Свойство.Имя может содержать только одно значение. В этом случае нам потребуется выполнить несколько отдельных ЛЕВЫХ СОЕДИНЕНИЙ с таблицей Справочник.Номенклатура.ДополнительныеРеквизиты.


ВЫБРАТЬ
    РеализацияТоваровУслугТовары.Ссылка КАК Ссылка,
    РеализацияТоваровУслугТовары.Номенклатура КАК Номенклатура,
    ЕСТЬNULL(ДопРеквизитОсновнаяГруппа.Значение, "") КАК ОсновнаяГруппа,
    ЕСТЬNULL(ДопРеквизитКласс.Значение, "") КАК Класс
ИЗ
    Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары
        ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура.ДополнительныеРеквизиты КАК ДопРеквизитОсновнаяГруппа
        ПО РеализацияТоваровУслугТовары.Номенклатура = ДопРеквизитОсновнаяГруппа.Ссылка
        И ДопРеквизитОсновнаяГруппа.Свойство.Имя = "ОсновнаяГруппа_2e29e30df745476fa8e61ecaea95e230"

        ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура.ДополнительныеРеквизиты КАК ДопРеквизитКласс
        ПО РеализацияТоваровУслугТовары.Номенклатура = ДопРеквизитКласс.Ссылка
        И ДопРеквизитКласс.Свойство.Имя = "Класс_f2f8a1e2b3c4d5e6f7a8b9c0d1e2f3g4" // Пример другого имени свойства
ГДЕ
    РеализацияТоваровУслугТовары.Ссылка = &Ссылка

В этом примере мы создаем два псевдонима для таблицы Справочник.Номенклатура.ДополнительныеРеквизиты и выполняем два отдельных ЛЕВЫХ СОЕДИНЕНИЯ, каждое со своим условием в секции ПО, что позволяет получить значения различных дополнительных реквизитов в отдельных полях.

Альтернативный подход: использование ВЫБОР КОГДА в секции ГДЕ (для отбора самих товаров)

Хотя основной задачей было вывести все записи и лишь условно присоединить свойства, иногда может возникнуть другая, смежная задача: вывести все услуги, а товары вывести только те, у которых дополнительные свойства имеют определенное значение. В этом случае, мы уже будем применять отбор на сами товары, а не только на присоединяемые свойства. Для такой задачи можно использовать конструкцию ВЫБОР КОГДА в секции ГДЕ.


ВЫБРАТЬ
    РеализацияТоваровУслугТовары.Ссылка КАК Ссылка,
    РеализацияТоваровУслугТовары.Номенклатура КАК Номенклатура,
    НоменклатураДополнительныеРеквизиты.Значение КАК Значение
ИЗ
    Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары
        ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура.ДополнительныеРеквизиты КАК НоменклатураДополнительныеРеквизиты
        ПО (РеализацияТоваровУслугТовары.Номенклатура = НоменклатураДополнительныеРеквизиты.Ссылка)
        И (НоменклатураДополнительныеРеквизиты.Свойство.Имя = "ОсновнаяГруппа_2e29e30df745476fa8e61ecaea95e230")
ГДЕ
    РеализацияТоваровУслугТовары.Ссылка = &Ссылка
    И ВЫБОР
        КОГДА РеализацияТоваровУслугТовары.Номенклатура.ТипНоменклатуры = ЗНАЧЕНИЕ(Перечисление.ТипыНоменклатуры.Товар)
            ТОГДА НоменклатураДополнительныеРеквизиты.Значение = &КакоеТоЗначение
        ИНАЧЕ ИСТИНА
    КОНЕЦ

В этом примере:

  1. Условие на тип дополнительного реквизита все равно остается в секции ПО для корректного присоединения.
  2. В секции ГДЕ мы добавляем конструкцию ВЫБОР КОГДА:
    • Если текущая позиция номенклатуры является товаром (РеализацияТоваровУслугТовары.Номенклатура.ТипНоменклатуры = ЗНАЧЕНИЕ(Перечисление.ТипыНоменклатуры.Товар)), то к ней применяется дополнительное условие НоменклатураДополнительныеРеквизиты.Значение = &КакоеТоЗначение. Это означает, что будут выбраны только те товары, у которых дополнительный реквизит "ОсновнаяГруппа" имеет конкретное значение.
    • Если же номенклатура не является товаром (т.е. это услуга), то условие ИНАЧЕ ИСТИНА всегда возвращает истину, что позволяет вывести все услуги без какого-либо дополнительного отбора по их свойствам.

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

Использование ТИП или ВидНоменклатуры для специфических условий

Для более точного контроля и разделения логики для товаров и услуг, особенно если нам нужно применять совершенно разные условия отбора или обработки к ним, полезно использовать поля типа ТипНоменклатуры или ВидНоменклатуры из справочника Номенклатура.

Например, в конструкции ВЫБОР КОГДА, как показано выше, мы уже обращались к РеализацияТоваровУслугТовары.Номенклатура.ТипНоменклатуры. Это позволяет явно различать товары от услуг и применять к ним разные правила.

В особо сложных случаях, когда требуется применять совершенно разные условия отбора к товарам и услугам, и даже их структуры выборки могут немного отличаться, можно использовать конструкцию ОБЪЕДИНИТЬ ВСЕ (UNION ALL), объединяя два отдельных запроса:

  1. Один запрос для услуг (без условий на доп. реквизиты или с минимальной выборкой).
  2. Второй запрос для товаров (с условиями на доп. реквизиты и их выборкой).

Этот метод обеспечивает максимальную гибкость, но может быть более сложным для понимания и поддержки.

Оптимизация запросов

Для повышения производительности запросов, особенно при работе с большими объемами данных, важно обратить внимание на индексацию полей. Убедитесь, что поля, используемые в условиях секций ГДЕ, ПО и виртуальных таблицах, проиндексированы в вашей конфигурации. Для условий в ПО ЛЕВОГО СОЕДИНЕНИЯ особенно рекомендуется индексировать поля в правой таблице, которые участвуют в связях (например, Справочник.Номенклатура.ДополнительныеРеквизиты.Ссылка и Справочник.Номенклатура.ДополнительныеРеквизиты.Свойство).

Заключение

Мы рассмотрели одну из распространенных задач при работе с запросами в 1С: выборочный вывод дополнительных реквизитов для одних видов номенклатуры (товаров), при сохранении всех записей других видов (услуг) из табличной части документа. Ключевым моментом является понимание различий между условиями в секциях ПО и ГДЕ при использовании ЛЕВОГО СОЕДИНЕНИЯ. Перемещение условия на дополнительные реквизиты из ГДЕ в ПО позволяет эффективно решить эту задачу, сохраняя все необходимые данные и избегая нежелательной фильтрации. Мы также обсудили, как выбирать несколько дополнительных реквизитов и рассмотрели альтернативный подход для более сложного выборочного отбора записей с помощью ВЫБОР КОГДА.

← На главную