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