Как в запросе 1С выбрать данные из одной временной таблицы, которых нет во второй?

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

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

В этой статье мы разберем основные причины, по которым запросы с операторами НЕ В могут возвращать лишние данные, и проанализируем наиболее эффективные варианты решения.

Ошибка 1: Использование фигурных скобок построителя отчетов в обычном запросе

Одной из главных причин, почему условие фильтрации игнорируется и запрос возвращает все строки, является использование фигурных скобок {ГДЕ ...}. Данная конструкция предназначен для работы с Построителем отчетов или Системой компоновки данных (СКД).

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

Рассмотрим неудачный пример текста запроса:


// НЕПРАВИЛЬНО: Фигурные скобки отсекают условие при обычном выполнении запроса
ВЫБРАТЬ
	ВТпопытка.ХарактеристикаСсылка,
	ВТ.ХарактеристикиНоменклатуры
ИЗ
	ВТ КАК ВТ,
	ВТпопытка КАК ВТпопытка
{ГДЕ
	(НЕ ВТпопытка.ХарактеристикаСсылка В (ВТ.ХарактеристикиНоменклатуры)) КАК Поле1}

Для исправления этой ошибки достаточно убрать фигурные скобки и псевдоним поля, оставив стандартное ключевое слово ГДЕ:


// ПРАВИЛЬНО: Использование стандартной секции ГДЕ
ГДЕ
	НЕ ВТпопытка.ХарактеристикаСсылка В (ВЫБРАТЬ ВТ.ХарактеристикиНоменклатуры ИЗ ВТ КАК ВТ)

Ошибка 2: Перемножение таблиц (Декартово произведение)

Обратите внимание на секцию ИЗ в примере выше: ИЗ ВТ КАК ВТ, ВТпопытка КАК ВТпопытка. Перечисление таблиц через запятую без явного связывания создает декартово произведение строк. Это раздувает выборку, замедляет выполнение запроса и мешает корректной работе условий. Для сопоставления двух временных таблиц всегда следует использовать явное соединение.

Решение 1: Использование ЛЕВОГО СОЕДИНЕНИЯ и проверки на NULL (Рекомендуемый способ)

Хотя оператор НЕ В синтаксически прост, с точки зрения производительности и особенностей работы СУБД предпочтительным является использование конструкции ЛЕВОЕ СОЕДИНЕНИЕ с проверкой ключевого поля присоединяемой таблицы на ЕСТЬ NULL (так называемый Anti-Join).

Особенности этого подхода:

  1. Высокая скорость работы: СУБД строит оптимальный план выполнения для соединения таблиц, что работает значительно быстрее, чем вложенный подзапрос с NOT IN.
  2. Безопасность к NULL: Если во вложенном подзапросе с НЕ В оказывается значение NULL, результат вычисления условия для всех строк станет неопределенным, и запрос вернет пустую выборку. Левое соединение застраховано от этой проблемы.

Проанализируем поэтапное построение такого запроса:

  1. Помещаем во временную таблицу ВТВсеХарактеристики полный список элементов из справочника.
  2. Собираем списки используемых элементов из табличных частей документов и помещаем их во временную таблицу ВТИспользуемыеХарактеристики с группировкой или ключом РАЗЛИЧНЫЕ.
  3. Соединяем первую таблицу со второй левым соединением и фильтруем результат условием ГДЕ ВТИспользованные.Характеристика ЕСТЬ NULL.

ВЫБРАТЬ
	ХарактеристикиНоменклатуры.Ссылка КАК ХарактеристикаСсылка,
	ХарактеристикиНоменклатуры.Владелец КАК Владелец
ПОМЕСТИТЬ ВТВсеХарактеристики
ИЗ
	Справочник.ХарактеристикиНоменклатуры КАК ХарактеристикиНоменклатуры
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ РАЗЛИЧНЫЕ
	ПоступлениеТоваровУслугТовары.Характеристика КАК Характеристика
ПОМЕСТИТЬ ВТИспользуемыеХарактеристики
ИЗ
	Документ.ПоступлениеТоваровУслуг.Товары КАК ПоступлениеТоваровУслугТовары

ОБЪЕДИНИТЬ

ВЫБРАТЬ
	ЗаказКлиентаТовары.Характеристика
ИЗ
	Документ.ЗаказКлиента.Товары КАК ЗаказКлиентаТовары
;

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

Решение 2: Использование подзапроса с оператором "НЕ В"

Если объем данных во временных таблицах невелик, допустимо использовать подзапрос с оператором НЕ В. Главное условие — вкладывать корректный запрос к временной таблице, а не просто указывать ее имя в скобках.


ВЫБРАТЬ
	ВТВсе.ХарактеристикаСсылка КАК ХарактеристикаСсылка,
	ВТВсе.Владелец КАК Владелец
ИЗ
	ВТВсеХарактеристики КАК ВТВсе
ГДЕ
	НЕ ВТВсе.ХарактеристикаСсылка В
		(ВЫБРАТЬ
			ВТИспользованные.Характеристика
		ИЗ
			ВТИспользуемыеХарактеристики КАК ВТИспользованные)

Обработка результатов запроса в коде 1С

После получения выборки неиспользуемых элементов их часто требуется обработать программно — например, установить пометку на удаление. Для обращения к объекту из выборки используется метод ПолучитьОбъект() или специализированная процедура УстановитьПометкуУдаления().

Разберем пример обхода результатов и применения пометки удаления:


Выборка = Запрос.Выполнить().Выбрать();

Пока Выборка.Следующий() Цикл
	// Получаем объект ссылочного типа по имени поля из запроса
	ОбъектХарактеристика = Выборка.ХарактеристикаСсылка.ПолучитьОбъект();
	
	Если ОбъектХарактеристика <> Неопределено Тогда
		// Устанавливаем пометку на удаление
		ОбъектХарактеристика.УстановитьПометкуУдаления(Истина);
	КонецЕсли;
КонецЦикла;

При обходе выборки важно убедиться, что имя поля, к которому происходит обращение в цикле (в данном примере ХарактеристикаСсылка), точно совпадает с псевдонимом поля в секции ВЫБРАТЬ итогового запроса. Ошибка «Поле объекта не обнаружено» возникают именно при расхождении имени реквизита в выборке и тексте запроса.

← На главную