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