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