Как в запросе 1С получить все товары, включая те, у которых дополнительный реквизит не заполнен?

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

При работе с базами данных 1С и построении запросов, мы часто сталкиваемся с необходимостью получить полный список элементов какого-либо справочника, например, Справочник.Номенклатура, и при этом вывести значения их дополнительных реквизитов. Однако, стандартные подходы могут выдать только те элементы, у которых эти дополнительные реквизиты заполнены. В этой статье мы подробно разберем, как построить запрос, чтобы получить все товары, включая те, у которых дополнительный реквизит еще не был заполнен, и правильно обработать такие случаи.

Анализ первоначальной проблемы: почему не все значения попадают в выборку?

Давайте рассмотрим типовую ситуацию, с которой сталкиваются многие разработчики и пользователи. Изначальный запрос, который мы часто пытаемся использовать для получения наименования номенклатуры и значения ее дополнительного реквизита, выглядит примерно так:


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

На первый взгляд, этот запрос кажется логичным. Мы выбираем наименование номенклатуры и значение ее дополнительного реквизита, а затем фильтруем по определенному свойству. Однако, если мы запустим этот запрос, то обнаружим, что в выборку попадает только та номенклатура, у которой дополнительное свойство заполнено. Почему так происходит?

Дело в том, что когда мы обращаемся к табличной части объекта (в данном случае, Номенклатура.ДополнительныеРеквизиты) и затем используем поля из этой табличной части в условии ГДЕ, система 1С неявно выполняет внутреннее соединение (ВНУТРЕННЕЕ СОЕДИНЕНИЕ). Это означает, что в результат запроса попадут только те строки, для которых существуют соответствующие записи в обеих соединяемых таблицах. Если для какой-либо номенклатуры в табличной части ДополнительныеРеквизиты нет записи с нужным &Свойством, или нет вообще никаких записей, такая номенклатура будет полностью исключена из выборки.

Таким образом, мы видим, что первоначальный подход не позволяет нам получить "все товары", как мы изначально хотели, а только "товары с заполненным реквизитом".

Решение проблемы: используем оператор ЛЕВОЕ СОЕДИНЕНИЕ

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

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

Разберем это подробнее:

  1. Мы начинаем выборку из основного справочника — Справочник.Номенклатура. Это будет наша "левая" таблица.
  2. Мы присоединяем к ней табличную часть ДополнительныеРеквизиты с помощью ЛЕВОЕ СОЕДИНЕНИЕ. Это будет наша "правая" таблица.
  3. В секции ПО мы указываем условие связи между таблицами: Номенклатура.Ссылка = ДопРеквизит.Ссылка.
  4. И самое главное: условие на конкретное свойство дополнительного реквизита (например, ДопРеквизит.Свойство = &Свойство) мы добавляем к условиям соединения в секции ПО с использованием оператора И.

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

Посмотрим на пример правильного запроса:

Пример запроса с ЛЕВЫМ СОЕДИНЕНИЕМ

Вот как будет выглядеть корректный запрос, который вернет все элементы номенклатуры и, если есть, значение указанного дополнительного реквизита:


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

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

Обработка NULL-значений с помощью функции ЕСТЬNULL()

Как мы уже выяснили, для тех товаров, у которых дополнительный реквизит отсутствует или не заполнен, поле ЗначениеДопРеквизита в результате нашего ЛЕВОГО СОЕДИНЕНИЯ будет иметь значение NULL. Часто нам нужно, чтобы вместо NULL выводилась пустая строка, текст "Не заполнено" или какое-то другое осмысленное значение. Для этого в запросах 1С мы используем функцию ЕСТЬNULL().

Функция ЕСТЬNULL(Выражение, ЗначениеПоУмолчанию) проверяет Выражение. Если оно имеет значение NULL, функция возвращает ЗначениеПоУмолчанию; в противном случае возвращается само Выражение.

Применим эту функцию к нашему запросу:


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

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

Выбор метода идентификации свойства: по ссылке или по заголовку?

В процессе обсуждения на форуме возник вопрос о том, как лучше указывать свойство дополнительного реквизита: по прямой ссылке на элемент плана видов характеристик (ДопРеквизит.Свойство = &Свойство) или по его строковому представлению, например, заголовку (ДопРеквизит.Свойство.Заголовок = &Имя).

Рассмотрим оба подхода:

1. Идентификация по ссылке: ДопРеквизит.Свойство = &Свойство

Это предпочтительный и наиболее надежный способ. Мы передаем в запрос параметр &Свойство, который является непосредственной ссылкой на элемент плана видов характеристик (например, ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("НужныйДопРеквизит") или просто используя ссылку на предопределенный элемент, если он есть).

  1. Производительность: Сравнение ссылок (идентификаторов) значительно быстрее, так как СУБД может использовать индексы по полю Свойство, что приводит к более высокой скорости выполнения запроса, особенно на больших объемах данных.
  2. Надежность: Этот метод устойчив к изменениям заголовков или имен дополнительных реквизитов. Если пользователь изменит "Заголовок" свойства в конфигураторе или в пользовательском режиме, ваш запрос, использующий ссылку, продолжит работать корректно.
  3. Уникальность: Ссылка всегда гарантирует уникальность свойства, даже если по какой-то причине появились два свойства с одинаковым заголовком.

2. Идентификация по заголовку: ДопРеквизит.Свойство.Заголовок = &Имя

Этот метод используется, когда мы передаем в запрос строковый параметр &Имя, который соответствует заголовку дополнительного реквизита (например, "Цвет" или "ГарантийныйСрок").

  1. Удобство: Для некоторых сценариев это может быть удобно, так как можно жестко прописать строковое имя свойства в коде или легко получить его из пользовательского ввода без предварительного поиска ссылки.
  2. Производительность: Сравнение по строковому полю (Заголовок) обычно менее производительно, чем сравнение по ссылке. В зависимости от СУБД и наличия индексов, это может привести к полному сканированию таблицы, что замедлит выполнение запроса.
  3. Надежность: Этот метод менее надежен, так как заголовок свойства может быть изменен пользователем в режиме 1С:Предприятия. При изменении заголовка запрос перестанет работать.
  4. Уникальность: Нет гарантии уникальности. Могут существовать разные свойства с одинаковыми заголовками, что приведет к непредсказуемому поведению запроса.

Мы настоятельно рекомендуем использовать идентификацию свойства по ссылке (ДопРеквизит.Свойство = &Свойство) для большинства задач, требующих стабильности и высокой производительности. Подготовка параметра со ссылкой может потребовать дополнительного шага (например, функции ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию("НужныйДопРеквизит")), но это окупается надежностью решения.

Итоговое решение и важные нюансы

Таким образом, для получения всех товаров, включая те, у которых дополнительный реквизит не заполнен, мы применяем следующую стратегию:

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

Следуя этим шагам, мы сможем построить надежный и эффективный запрос, который полностью соответствует поставленной задаче и предоставляет полную информацию о всех элементах справочника, независимо от заполненности их дополнительных реквизитов. Мы рассмотрели все ключевые аспекты и теперь вы сможете уверенно применять этот подход в своих разработках на платформе 1С.

← На главную